105 lines
2.4 KiB
Python
105 lines
2.4 KiB
Python
"""File type detection for rp."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
EXTENSION_MAP: dict[str, str] = {
|
|
".md": "markdown",
|
|
".json": "json",
|
|
".py": "python",
|
|
".pyw": "python",
|
|
".js": "javascript",
|
|
".mjs": "javascript",
|
|
".ts": "typescript",
|
|
".tsx": "typescript",
|
|
".jsx": "jsx",
|
|
".css": "css",
|
|
".scss": "scss",
|
|
".html": "html",
|
|
".htm": "html",
|
|
".xml": "xml",
|
|
".yaml": "yaml",
|
|
".yml": "yaml",
|
|
".toml": "toml",
|
|
".sh": "bash",
|
|
".bash": "bash",
|
|
".zsh": "bash",
|
|
".sql": "sql",
|
|
".rs": "rust",
|
|
".go": "go",
|
|
".java": "java",
|
|
".c": "c",
|
|
".h": "c",
|
|
".cpp": "cpp",
|
|
".hpp": "cpp",
|
|
".cc": "cpp",
|
|
".rb": "ruby",
|
|
".php": "php",
|
|
".swift": "swift",
|
|
".kt": "kotlin",
|
|
".r": "r",
|
|
".lua": "lua",
|
|
".dockerfile": "docker",
|
|
".ini": "ini",
|
|
".cfg": "ini",
|
|
".diff": "diff",
|
|
".patch": "diff",
|
|
".tex": "latex",
|
|
}
|
|
|
|
_FILENAME_MAP: dict[str, str] = {
|
|
"Dockerfile": "docker",
|
|
"Makefile": "make",
|
|
}
|
|
|
|
|
|
def detect_type(
|
|
path: str | None = None,
|
|
content: str | None = None,
|
|
explicit_type: str | None = None,
|
|
) -> str:
|
|
"""Detect the file type for syntax highlighting.
|
|
|
|
Detection priority:
|
|
1. Explicit type if provided
|
|
2. Filename-based mapping (for example, Dockerfile)
|
|
3. Extension-based mapping
|
|
4. JSON if content starts with '{' or '[' and parses successfully
|
|
5. Fallback to 'text'
|
|
|
|
Args:
|
|
path: File path for filename and extension detection.
|
|
content: File content for JSON detection.
|
|
explicit_type: Override auto-detection with explicit type.
|
|
|
|
Returns:
|
|
Pygments lexer name for the detected file type.
|
|
"""
|
|
if explicit_type is not None:
|
|
return explicit_type
|
|
|
|
if path is not None:
|
|
p = Path(path)
|
|
name = p.name
|
|
if name in _FILENAME_MAP:
|
|
return _FILENAME_MAP[name]
|
|
suffix = p.suffix.lower()
|
|
if suffix in EXTENSION_MAP:
|
|
return EXTENSION_MAP[suffix]
|
|
|
|
if content is not None:
|
|
stripped = content.strip()
|
|
if stripped and (
|
|
(stripped[0] == "{" and stripped[-1] == "}")
|
|
or (stripped[0] == "[" and stripped[-1] == "]")
|
|
):
|
|
try:
|
|
json.loads(stripped)
|
|
return "json"
|
|
except (json.JSONDecodeError, ValueError):
|
|
pass
|
|
|
|
return "text"
|