import collections
import re


def key_assignments_from_file(file_path):
    """
    Return the key assignments found in the given file, as a
    collections.OrderedDict mapping each key to its text meaning.

    The file syntax and semantics are detailed in
    AnnotateShell.do_load_keys().

    This function is meant to be primarily used from
    AnnotateShell.do_load_keys().

    file_path -- file path, as a string.
    """

    # It is useful to keep the annotations in the same order as in the
    # file: the user can more easily recognize their list.
    key_assignments = collections.OrderedDict()

    with open(file_path) as keys_file:
        for (line_num, line) in enumerate(keys_file, 1):

            line = line.rstrip()

            # Empty lines and comments are skipped:
            if not line or line.startswith("#"):
                continue

            # Some characters are reserved and therefore cannot be
            # chosen by the user. The text meaning cannot be empty:
            # otherwise, some annotations would look empty, when
            # displayed during the "annotate" command process.
            match = re.match("([^\s0-9<>\x7f\-])\s*(.+)", line)
            if not match:
                raise Exception("Syntax error on line {}:\n{}".format(
                    line_num, line))

            (key, text) = match.groups()

            # Sanity check, for the benefit of the user:
            if key in key_assignments:
                raise Exception("Key defined more than once: {}.".format(key))
            key_assignments[key] = text

    return key_assignments