diff --git a/docs/changelog.rst b/docs/changelog.rst index 6a48675a7..4810b84ec 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -24,6 +24,9 @@ To update |kitty|, :doc:`follow the instructions `. - Fix selections created by dragging upwards not being auto-cleared when screen contents change (:pull:`3028`) +- Allow adding MIME definitions to kitty by placing a ``mime.types`` file in + the kitty config directory (:iss:`3056`) + 0.19.1 [2020-10-06] ------------------- diff --git a/docs/open_actions.rst b/docs/open_actions.rst index 2479598af..06582b957 100644 --- a/docs/open_actions.rst +++ b/docs/open_actions.rst @@ -81,7 +81,11 @@ lines. The various available criteria are: ``mime`` A comma separated list of MIME types, for example: ``text/*, image/*, - application/pdf`` + application/pdf``. You can add MIME types to kitty by creating the + :file:`mime.types` in the kitty configuration directory. Useful if your + system MIME database does not have definitions you need. This file is + in the standard format of one definition per line, like: ``text/plain rst + md``. ``ext`` A comma separated list of file extensions, for example: ``jpeg, tar.gz`` diff --git a/kittens/diff/collect.py b/kittens/diff/collect.py index 01c15486c..a10c98ec3 100644 --- a/kittens/diff/collect.py +++ b/kittens/diff/collect.py @@ -7,7 +7,7 @@ import re from contextlib import suppress from functools import lru_cache from hashlib import md5 -from mimetypes import guess_type +from kitty.guess_mime_type import guess_type from typing import TYPE_CHECKING, Dict, List, Set, Optional, Iterator, Tuple, Union if TYPE_CHECKING: @@ -136,7 +136,7 @@ def sanitize(text: str) -> str: @lru_cache(maxsize=1024) def mime_type_for_path(path: str) -> str: - return guess_type(path)[0] or 'application/octet-stream' + return guess_type(path) or 'application/octet-stream' @lru_cache(maxsize=1024) diff --git a/kittens/icat/main.py b/kittens/icat/main.py index 45d3d60da..1282431da 100755 --- a/kittens/icat/main.py +++ b/kittens/icat/main.py @@ -3,7 +3,6 @@ # License: GPL v3 Copyright: 2017, Kovid Goyal import contextlib -import mimetypes import os import re import socket @@ -18,6 +17,7 @@ from typing import ( Dict, Generator, List, NamedTuple, Optional, Pattern, Tuple, Union ) +from kitty.guess_mime_type import guess_type from kitty.cli import parse_args from kitty.cli_stub import IcatCLIOptions from kitty.constants import appname @@ -273,7 +273,7 @@ def process(path: str, args: IcatCLIOptions, parsed_opts: ParsedOpts, is_tempfil def scan(d: str) -> Generator[Tuple[str, str], None, None]: for dirpath, dirnames, filenames in os.walk(d): for f in filenames: - mt = mimetypes.guess_type(f)[0] + mt = guess_type(f) if mt and mt.startswith('image/'): yield os.path.join(dirpath, f), mt diff --git a/kitty/complete.py b/kitty/complete.py index dbdce61b2..b4b32bdda 100644 --- a/kitty/complete.py +++ b/kitty/complete.py @@ -394,10 +394,10 @@ def complete_files_and_dirs( def complete_icat_args(ans: Completions, opt: Optional[OptionDict], prefix: str, unknown_args: Delegate) -> None: - from mimetypes import guess_type + from .guess_mime_type import guess_type def icat_file_predicate(filename: str) -> bool: - mt = guess_type(filename)[0] + mt = guess_type(filename) if mt and mt.startswith('image/'): return True return False diff --git a/kitty/guess_mime_type.py b/kitty/guess_mime_type.py new file mode 100644 index 000000000..b900bcbd0 --- /dev/null +++ b/kitty/guess_mime_type.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +# License: GPLv3 Copyright: 2020, Kovid Goyal + +import os +from contextlib import suppress +from typing import Optional + +known_extensions = { + 'asciidoc': 'text/asciidoctor', + 'conf': 'text/config', + 'md': 'text/markdown', + 'pyj': 'text/rapydscript-ng', + 'recipe': 'text/python', + 'rst': 'text/restructured-text', + 'toml': 'text/toml', + 'vim': 'text/vim', + 'yaml': 'text/yaml', +} + + +def is_rc_file(path: str) -> bool: + name = os.path.basename(path) + return '.' not in name and name.endswith('rc') + + +def guess_type(path: str) -> Optional[str]: + if not hasattr(guess_type, 'inited'): + setattr(guess_type, 'inited', True) + from mimetypes import init + init(None) + from kitty.constants import config_dir + local_defs = os.path.join(config_dir, 'mime.types') + if os.path.exists(local_defs): + init((local_defs,)) + from mimetypes import guess_type as stdlib_guess_type + mt = None + with suppress(Exception): + mt = stdlib_guess_type(path)[0] + if not mt: + ext = path.rpartition('.')[-1].lower() + mt = known_extensions.get(ext) + if not mt and is_rc_file(path): + mt = 'text/plain' + return mt diff --git a/kitty/open_actions.py b/kitty/open_actions.py index 5dea14bcf..0c6a0532f 100644 --- a/kitty/open_actions.py +++ b/kitty/open_actions.py @@ -17,6 +17,7 @@ from .config import KeyAction, parse_key_action from .constants import config_dir from .typing import MatchType from .utils import expandvars, log_error +from .guess_mime_type import guess_type class MatchCriteria(NamedTuple): @@ -75,12 +76,8 @@ def url_matches_criterion(purl: 'ParseResult', url: str, unquoted_path: str, mc: if mc.type == 'mime': import fnmatch - from mimetypes import guess_type - try: - mt = guess_type(unquoted_path)[0] - except Exception: - return False - if mt is None: + mt = guess_type(unquoted_path) + if not mt: return False mt = mt.lower() for mpat in mc.value.split(','):