<commit_before>import urllib.parse

import webbrowser
from xml.etree import ElementTree

import sublime

import SublimeHaskell.sublime_haskell_common as Common
import SublimeHaskell.internals.utils as Utils
import SublimeHaskell.internals.unicode_opers as UnicodeOpers
import SublimeHaskell.symbols as symbols
import SublimeHaskell.internals.backend_mgr as BackendManager
import SublimeHaskell.parseoutput as ParseOutput
import SublimeHaskell.types as types


# Unused module variable:
# style_header = "<style>" \
#     "a { text-decoration: underline; }" \
#     ".type { color: red; }" \
#     ".tyvar { color: blue; }" \
#     ".operator { color: green; }" \
#     ".comment { color: gray; font-style: italic; }" \
#     ".docs { color: gray; }" \
#     "</style>"


class Styles(object):
    """
    Loads and holds cache of scheme styles
    Also generates style header
    """
    def __init__(self):
        self.schemes = {}

    CSS_CLASSES = {
        'comment': 'comment',
        'function': 'entity.name.function',
        'type': 'entity.name.type',
        'operator': 'keyword.operator',
        'keyword': 'keyword.declaration',
        'tyvar': 'variable.generic',
        'error': 'sublimehaskell.mark.error',
        'warning': 'sublimehaskell.mark.warning',
        'hint': 'sublimehaskell.mark.hint'
    }

    def load_scheme(self, scheme_path):
        if scheme_path not in self.schemes:
            scheme_res = sublime.load_resource(scheme_path)
            if scheme_res:
                # Go through all styles and collect scope/foreground/fontStyle etc.
                self.schemes[scheme_path] = self.collect_scheme(ElementTree.fromstring(scheme_res))

        return self.schemes.get(scheme_path, {})

    def collect_scheme(self, scheme_tree):
        scheme = {}
        for style in scheme_tree.findall(".//dict[key='scope']"):
            try:
                cur_style = {}
                cur_tag = None
                for elem in style.iter():
                    if elem.tag == 'key':
                        cur_tag = elem.text  # We are going to fill it next time
                    elif elem.tag == 'string' and cur_tag is not None:
                        cur_style[cur_tag] = elem.text
                        cur_tag = None
                if 'scope' in cur_style:
                    scheme[cur_style['scope']] = cur_style
            except ValueError:
                pass
        return scheme

    def gen_style(self, scheme_path):
        scheme = self.load_scheme(scheme_path)

        parts = []
        parts.append("<style>")
        parts.append("a { text-decoration: underline; }")
        # generate CSS style for each class
        for cls, scope in self.CSS_CLASSES.items():
            # find scope or its parent in scheme
            scope_parts = scope.split('.')
            scopes = ['.'.join(scope_parts[0:i+1]) for i in range(0, len(scope_parts))]
            for css_scope in reversed(scopes):
                if css_scope in scheme:  # Found some scope, fill style class
                    style_parts = []
                    if 'foreground' in scheme[css_scope]:
                        style_parts.append("color: {0}".format(scheme[css_scope]['foreground']))
                    if 'fontStyle' in scheme[css_scope]:
                        style_parts.append("font-style: {0}".format(scheme[css_scope]['fontStyle']))
                    parts.append(".{0} {{ {1} }}".format(cls, "; ".join(style_parts)))
                    break
        parts.append("</style>")
        return "".join(parts)


class SublimeHaskellHoverPopup(object):
    # HTML style formatting
    STYLES = Styles()

    def __init__(self, view, filename, point, hover_zone):
        super().__init__()
        self.view = view
        self.filename = filename
        self.point = point
        self.hover_zone = hover_zone
        self.line = view.rowcol(point)[0]
        self.shown = False


    def do_hover(self):
        if self.hover_zone == sublime.HOVER_TEXT:
            line, column = self.view.rowcol(self.point)
            qsymbol = Common.get_qualified_symbol_at_point(self.view, self.point)
            ## print('hover: qualified symbol {0}'.format(qsymbol))
            module_word = qsymbol.module
            ident = qsymbol.name

            if module_word is not None and ident is None:
                # TODO: Any ideas for popup about module?
                pass
            elif ident is not None:
                whois_name = qsymbol.qualified_name()
                full_name = qsymbol.full_name()

                # Try get type of hovered symbol<commit_msg>Run infer types on hover if `enable_infer_types` enabled<commit_after>import urllib.parse

import webbrowser
from xml.etree import ElementTree

import sublime

import SublimeHaskell.sublime_haskell_common as Common
import SublimeHaskell.internals.utils as Utils
import SublimeHaskell.internals.unicode_opers as UnicodeOpers
import SublimeHaskell.symbols as symbols
import SublimeHaskell.internals.backend_mgr as BackendManager
import SublimeHaskell.internals.settings as Settings
import SublimeHaskell.parseoutput as ParseOutput
import SublimeHaskell.types as types


# Unused module variable:
# style_header = "<style>" \
#     "a { text-decoration: underline; }" \
#     ".type { color: red; }" \
#     ".tyvar { color: blue; }" \
#     ".operator { color: green; }" \
#     ".comment { color: gray; font-style: italic; }" \
#     ".docs { color: gray; }" \
#     "</style>"


class Styles(object):
    """
    Loads and holds cache of scheme styles
    Also generates style header
    """
    def __init__(self):
        self.schemes = {}

    CSS_CLASSES = {
        'comment': 'comment',
        'function': 'entity.name.function',
        'type': 'entity.name.type',
        'operator': 'keyword.operator',
        'keyword': 'keyword.declaration',
        'tyvar': 'variable.generic',
        'error': 'sublimehaskell.mark.error',
        'warning': 'sublimehaskell.mark.warning',
        'hint': 'sublimehaskell.mark.hint'
    }

    def load_scheme(self, scheme_path):
        if scheme_path not in self.schemes:
            scheme_res = sublime.load_resource(scheme_path)
            if scheme_res:
                # Go through all styles and collect scope/foreground/fontStyle etc.
                self.schemes[scheme_path] = self.collect_scheme(ElementTree.fromstring(scheme_res))

        return self.schemes.get(scheme_path, {})

    def collect_scheme(self, scheme_tree):
        scheme = {}
        for style in scheme_tree.findall(".//dict[key='scope']"):
            try:
                cur_style = {}
                cur_tag = None
                for elem in style.iter():
                    if elem.tag == 'key':
                        cur_tag = elem.text  # We are going to fill it next time
                    elif elem.tag == 'string' and cur_tag is not None:
                        cur_style[cur_tag] = elem.text
                        cur_tag = None
                if 'scope' in cur_style:
                    scheme[cur_style['scope']] = cur_style
            except ValueError:
                pass
        return scheme

    def gen_style(self, scheme_path):
        scheme = self.load_scheme(scheme_path)

        parts = []
        parts.append("<style>")
        parts.append("a { text-decoration: underline; }")
        # generate CSS style for each class
        for cls, scope in self.CSS_CLASSES.items():
            # find scope or its parent in scheme
            scope_parts = scope.split('.')
            scopes = ['.'.join(scope_parts[0:i+1]) for i in range(0, len(scope_parts))]
            for css_scope in reversed(scopes):
                if css_scope in scheme:  # Found some scope, fill style class
                    style_parts = []
                    if 'foreground' in scheme[css_scope]:
                        style_parts.append("color: {0}".format(scheme[css_scope]['foreground']))
                    if 'fontStyle' in scheme[css_scope]:
                        style_parts.append("font-style: {0}".format(scheme[css_scope]['fontStyle']))
                    parts.append(".{0} {{ {1} }}".format(cls, "; ".join(style_parts)))
                    break
        parts.append("</style>")
        return "".join(parts)


class SublimeHaskellHoverPopup(object):
    # HTML style formatting
    STYLES = Styles()

    def __init__(self, view, filename, point, hover_zone):
        super().__init__()
        self.view = view
        self.filename = filename
        self.point = point
        self.hover_zone = hover_zone
        self.line = view.rowcol(point)[0]
        self.shown = False


    def do_hover(self):
        if self.hover_zone == sublime.HOVER_TEXT:
            line, column = self.view.rowcol(self.point)
            qsymbol = Common.get_qualified_symbol_at_point(self.view, self.point)
            ## print('hover: qualified symbol {0}'.format(qsymbol))
            module_word = qsymbol.module
            ident = qsymbol.name

            if module_word is not None and ident is None:
                # TODO: Any ideas for popup about module?
                pass
            elif ident is not None:
                whois_name = qsymbol.qualified_name()
                full_name = qsymbol.full_name()

                # Infer types in backgroup (if not any)
                if Settings.PLUGIN.enable_infer_types:
                    BackendManager.active_backend().infer(files=[self.filename])

                # Try get type of hovered symbol
{'fraction_non_alphanumeric': 0.10777440619441248, 'fraction_numerical': 0.0018834362247567228, 'mean_word_length': 2.0371782650142993, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '599189', 'n_tokens_mistral': 2809, 'n_tokens_neox': 2678, 'n_words': 668}