<commit_before>import sublime
import sublime_plugin

from bisect import bisect
import os

import sys
from imp import reload


# If our submodules have previously been loaded, reload them now before we
# proceed to ensure everything is up to date.
modules = ["OverrideAudit.lib.packages", "OverrideAudit.lib.output_view"]
for module in modules:
    if module in sys.modules:
        reload(sys.modules[module])


from .lib.packages import PackageInfo, PackageList
from .lib.output_view import output_to_view


###----------------------------------------------------------------------------


def plugin_loaded():
    """
    Initialize the PackageInfo class at plugin load time.
    """
    PackageInfo.init()


def _packages_with_overrides(pkg_list, name_list=None):
    """
    Collect a list of package names from the given package list for which there
    is at least a single (simple) override file and which is not in the list of
    packages to ignore overrides in.

    Optionally, if name_list is provided, the list of package names will be
    filtered to only include packages whose name also exists in the name list.
    """
    settings = sublime.load_settings("OverrideAudit.sublime-settings")
    ignored = settings.get("ignore_overrides_in", [])

    items = [name for name, pkg in pkg_list if len(pkg.override_files()) > 0
                                               and name not in ignored]

    if name_list is not None:
        items = list(filter(lambda name: name in name_list, items))

    return items


def _decorate_package_name(pkg_info):
    """
    Decorate the name of the provided package with a prefix that describes its
    status and optionally also a suffix if it is a complete override or is
    expired.
    """
    suffix = ""
    pkg_name = pkg_info.name

    pkg_name = "[{}]".format(pkg_name) if pkg_info.is_disabled else pkg_name

    if pkg_info.has_possible_overrides(simple=False):
        suffix += " <Complete Override>"

    if bool(pkg_info.expired_override_files(simple=False)):
        suffix += " [EXPIRED]"

    return "[{}{}{}] {}{}".format(
               "S" if pkg_info.shipped_path is not None else " ",
               "I" if pkg_info.installed_path is not None else " ",
               "U" if pkg_info.unpacked_path is not None else " ",
               pkg_name,
               suffix)


def _apply_override_settings(view, pkg_name, override, is_diff):
    """
    Apply view settings marking the view as an override view.
    """
    view.settings().set("override_audit_package", pkg_name)
    view.settings().set("override_audit_override", override)
    view.settings().set("override_audit_diff", is_diff)


def _remove_override_settings(view):
    """
    Remove view settings marking the view as an override view.
    """
    view.settings().erase("override_audit_package")
    view.settings().erase("override_audit_override")
    view.settings().erase("override_audit_diff")


def _apply_report_settings(view, report_type):
    """
    Apply view settings marking the view as a report view.
    """
    view.settings().set("override_audit_report", True)
    view.settings().set("override_audit_report_type", report_type)


def _open_override_file(window, pkg_name, override):
    """
    Open the provided override from the given package name.
    """
    filename = os.path.join(sublime.packages_path(), pkg_name, override)
    window.open_file(filename)


def _delete_override_file(window, pkg_name, override):
    """
    Delete the provided override from the given package name.
    """
    # Import send2trash on demand; see Default/side_bar.py.
    import Default.send2trash as send2trash

    settings = sublime.load_settings("OverrideAudit.sublime-settings")
    confirm = settings.get("confirm_deletion", True)

    relative_name = os.path.join(pkg_name, override)
    full_name = os.path.join(sublime.packages_path(), relative_name)
    if os.path.isfile(full_name):
        if confirm:
            msg = "Confirm deletion:\n\n{}".format(relative_name)

        if (confirm is False or
                sublime.yes_no_cancel_dialog(msg) == sublime.DIALOG_YES):
            send2trash.send2trash(full_name)
            window.status_message("Deleted {}".format(relative_name))


def _diff_override_file(window, pkg_info, override,
                        diff_only=False, force_reuse=False):
    """
    Generate a diff for the given package and override.
    """
    settings = sublime.load_settings("OverrideAudit.sublime-settings")
    context_lines = settings.get("diff_context_lines", 3)

    action = "diff" if diff_only else settings.get("diff_unchanged", "diff")

    if force_reuse:
        reuse, clear = True, True
    else:
        reuse = settings.get("reuse_views", True)
        clear = settings.get("clear_existing", True)

    diff_info = pkg_info.override_diff(override, context_lines,
                                       binary_result="<File is binary>")

    if diff_info is None:
        return

    if diff_info == "":
        sublime.status_message("No changes detected in override")

        if action == "open":
            return _open_override_file(window, pkg_info.name, override)

        elif action == "ignore":
            return

    title = "Override of %s" % os.path.join(pkg_info.name, override)
    content = "No differences found" if diff_info == "" else diff_info
    view = output_to_view(window, title, content, reuse, clear,
                          "Packages/Diff/Diff.sublime-syntax")

    _apply_override_settings(view, pkg_info.name, override, True)


###----------------------------------------------------------------------------


class OverrideAuditPackageReportCommand(sublime_plugin.WindowCommand):
    """
    Generate a tabular report of all installed packages and their state.
    """
    def run(self, force_reuse=False):
        pkg_list = PackageList()
        pkg_counts = pkg_list.package_counts()<commit_msg>Include an initial spinner class<commit_after>import sublime
import sublime_plugin

from bisect import bisect
import os

import sys
from imp import reload


# If our submodules have previously been loaded, reload them now before we
# proceed to ensure everything is up to date.
modules = ["OverrideAudit.lib.packages", "OverrideAudit.lib.output_view"]
for module in modules:
    if module in sys.modules:
        reload(sys.modules[module])


from .lib.packages import PackageInfo, PackageList
from .lib.output_view import output_to_view


###----------------------------------------------------------------------------


def plugin_loaded():
    """
    Initialize the PackageInfo class at plugin load time.
    """
    PackageInfo.init()


def _packages_with_overrides(pkg_list, name_list=None):
    """
    Collect a list of package names from the given package list for which there
    is at least a single (simple) override file and which is not in the list of
    packages to ignore overrides in.

    Optionally, if name_list is provided, the list of package names will be
    filtered to only include packages whose name also exists in the name list.
    """
    settings = sublime.load_settings("OverrideAudit.sublime-settings")
    ignored = settings.get("ignore_overrides_in", [])

    items = [name for name, pkg in pkg_list if len(pkg.override_files()) > 0
                                               and name not in ignored]

    if name_list is not None:
        items = list(filter(lambda name: name in name_list, items))

    return items


def _decorate_package_name(pkg_info):
    """
    Decorate the name of the provided package with a prefix that describes its
    status and optionally also a suffix if it is a complete override or is
    expired.
    """
    suffix = ""
    pkg_name = pkg_info.name

    pkg_name = "[{}]".format(pkg_name) if pkg_info.is_disabled else pkg_name

    if pkg_info.has_possible_overrides(simple=False):
        suffix += " <Complete Override>"

    if bool(pkg_info.expired_override_files(simple=False)):
        suffix += " [EXPIRED]"

    return "[{}{}{}] {}{}".format(
               "S" if pkg_info.shipped_path is not None else " ",
               "I" if pkg_info.installed_path is not None else " ",
               "U" if pkg_info.unpacked_path is not None else " ",
               pkg_name,
               suffix)


def _apply_override_settings(view, pkg_name, override, is_diff):
    """
    Apply view settings marking the view as an override view.
    """
    view.settings().set("override_audit_package", pkg_name)
    view.settings().set("override_audit_override", override)
    view.settings().set("override_audit_diff", is_diff)


def _remove_override_settings(view):
    """
    Remove view settings marking the view as an override view.
    """
    view.settings().erase("override_audit_package")
    view.settings().erase("override_audit_override")
    view.settings().erase("override_audit_diff")


def _apply_report_settings(view, report_type):
    """
    Apply view settings marking the view as a report view.
    """
    view.settings().set("override_audit_report", True)
    view.settings().set("override_audit_report_type", report_type)


def _open_override_file(window, pkg_name, override):
    """
    Open the provided override from the given package name.
    """
    filename = os.path.join(sublime.packages_path(), pkg_name, override)
    window.open_file(filename)


def _delete_override_file(window, pkg_name, override):
    """
    Delete the provided override from the given package name.
    """
    # Import send2trash on demand; see Default/side_bar.py.
    import Default.send2trash as send2trash

    settings = sublime.load_settings("OverrideAudit.sublime-settings")
    confirm = settings.get("confirm_deletion", True)

    relative_name = os.path.join(pkg_name, override)
    full_name = os.path.join(sublime.packages_path(), relative_name)
    if os.path.isfile(full_name):
        if confirm:
            msg = "Confirm deletion:\n\n{}".format(relative_name)

        if (confirm is False or
                sublime.yes_no_cancel_dialog(msg) == sublime.DIALOG_YES):
            send2trash.send2trash(full_name)
            window.status_message("Deleted {}".format(relative_name))


def _diff_override_file(window, pkg_info, override,
                        diff_only=False, force_reuse=False):
    """
    Generate a diff for the given package and override.
    """
    settings = sublime.load_settings("OverrideAudit.sublime-settings")
    context_lines = settings.get("diff_context_lines", 3)

    action = "diff" if diff_only else settings.get("diff_unchanged", "diff")

    if force_reuse:
        reuse, clear = True, True
    else:
        reuse = settings.get("reuse_views", True)
        clear = settings.get("clear_existing", True)

    diff_info = pkg_info.override_diff(override, context_lines,
                                       binary_result="<File is binary>")

    if diff_info is None:
        return

    if diff_info == "":
        sublime.status_message("No changes detected in override")

        if action == "open":
            return _open_override_file(window, pkg_info.name, override)

        elif action == "ignore":
            return

    title = "Override of %s" % os.path.join(pkg_info.name, override)
    content = "No differences found" if diff_info == "" else diff_info
    view = output_to_view(window, title, content, reuse, clear,
                          "Packages/Diff/Diff.sublime-syntax")

    _apply_override_settings(view, pkg_info.name, override, True)


###----------------------------------------------------------------------------


class Spinner():
    """
    A simple spinner that follows the active view in the provided window which
    self terminates when the provided thread is no longer running.
    """
    spin_text = "|/-\\"

    def __init__(self, window, thread, prefix):
        self.window = window
        self.thread = thread
        self.prefix = prefix
        self.tick_view = None

        sublime.set_timeout(lambda: self.tick(0), 250)

    def tick(self, position):
        current_view = self.window.active_view()

        if self.tick_view is not None and current_view != self.tick_view:
            self.tick_view.erase_status("oa_spinner")
            self.tick_view is None

        if not self.thread.is_alive():
            current_view.erase_status("oa_spinner")
            return

        text = "%s [%s]" % (self.prefix, self.spin_text[position])
        position = (position + 1) % len(self.spin_text)

        current_view.set_status("oa_spinner", text)
        if self.tick_view is None:
            self.tick_view = current_view

        sublime.set_timeout(lambda: self.tick(position), 250)


###----------------------------------------------------------------------------


class OverrideAuditPackageReportCommand(sublime_plugin.WindowCommand):
    """
    Generate a tabular report of all installed packages and their state.
    """
    def run(self, force_reuse=False):
        pkg_list = PackageList()
        pkg_counts = pkg_list.package_counts()
{'fraction_non_alphanumeric': 0.12379715900412402, 'fraction_numerical': 0.0016801588513823125, 'mean_word_length': 2.6835443037974684, 'pattern_counts': {'":': 6, '<': 7, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2082333', 'n_tokens_mistral': 3829, 'n_tokens_neox': 3661, 'n_words': 1033}