From 75a94bcd96f74be024eb0a28de87ca9e15c4c995 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 25 Oct 2020 13:42:11 +0530 Subject: [PATCH] Improving MIME type detection for some common file types when they are missing from the system MIME database Also allow the user to specify their own database via mime.types in the kitty config directory. See #3056 --- docs/changelog.rst | 3 +++ docs/open_actions.rst | 6 +++++- kittens/diff/collect.py | 4 ++-- kittens/icat/main.py | 4 ++-- kitty/complete.py | 4 ++-- kitty/guess_mime_type.py | 45 ++++++++++++++++++++++++++++++++++++++++ kitty/open_actions.py | 9 +++----- 7 files changed, 62 insertions(+), 13 deletions(-) create mode 100644 kitty/guess_mime_type.py 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(','):