diffctx 1.12.1

Selects the minimum code an LLM needs to review a git diff: walks the dependency graph outward from changed lines and stops when extra context stops paying for itself
Documentation
use once_cell::sync::Lazy;
use rustc_hash::FxHashMap;
use std::path::Path;

pub static EXTENSION_TO_LANGUAGE: Lazy<FxHashMap<&'static str, &'static str>> = Lazy::new(|| {
    let entries: &[(&str, &str)] = &[
        (".py", "python"),
        (".pyw", "python"),
        (".pyi", "python"),
        (".js", "javascript"),
        (".mjs", "javascript"),
        (".cjs", "javascript"),
        (".jsx", "jsx"),
        (".ts", "typescript"),
        (".tsx", "tsx"),
        (".mts", "typescript"),
        (".cts", "typescript"),
        (".json", "json"),
        (".yaml", "yaml"),
        (".yml", "yaml"),
        (".toml", "toml"),
        (".md", "markdown"),
        (".markdown", "markdown"),
        (".html", "html"),
        (".htm", "html"),
        (".css", "css"),
        (".scss", "scss"),
        (".less", "less"),
        (".xml", "xml"),
        (".svg", "xml"),
        (".sh", "bash"),
        (".bash", "bash"),
        (".zsh", "zsh"),
        (".fish", "fish"),
        (".ksh", "bash"),
        (".ps1", "powershell"),
        (".psm1", "powershell"),
        (".psd1", "powershell"),
        (".bat", "batch"),
        (".cmd", "batch"),
        (".c", "c"),
        (".h", "c"),
        (".cpp", "cpp"),
        (".cc", "cpp"),
        (".cxx", "cpp"),
        (".hpp", "cpp"),
        (".hh", "cpp"),
        (".hxx", "cpp"),
        (".cs", "csharp"),
        (".fs", "fsharp"),
        (".fsi", "fsharp"),
        (".fsx", "fsharp"),
        (".java", "java"),
        (".kt", "kotlin"),
        (".kts", "kotlin"),
        (".scala", "scala"),
        (".sc", "scala"),
        (".go", "go"),
        (".rs", "rust"),
        (".rb", "ruby"),
        (".rake", "ruby"),
        (".gemspec", "ruby"),
        (".php", "php"),
        (".swift", "swift"),
        (".m", "objectivec"),
        (".mm", "objectivec"),
        (".r", "r"),
        (".lua", "lua"),
        (".pl", "perl"),
        (".pm", "perl"),
        (".ex", "elixir"),
        (".exs", "elixir"),
        (".erl", "erlang"),
        (".hrl", "erlang"),
        (".hs", "haskell"),
        (".lhs", "haskell"),
        (".ml", "ocaml"),
        (".mli", "ocaml"),
        (".clj", "clojure"),
        (".cljs", "clojure"),
        (".cljc", "clojure"),
        (".sql", "sql"),
        (".graphql", "graphql"),
        (".gql", "graphql"),
        (".proto", "protobuf"),
        (".dockerfile", "dockerfile"),
        (".tf", "terraform"),
        (".hcl", "hcl"),
        (".vim", "vim"),
        (".el", "elisp"),
        (".lisp", "lisp"),
        (".scm", "scheme"),
        (".rkt", "racket"),
        (".zig", "zig"),
        (".nim", "nim"),
        (".v", "v"),
        (".sv", "systemverilog"),
        (".vhd", "vhdl"),
        (".vhdl", "vhdl"),
        (".d", "d"),
        (".dart", "dart"),
        (".groovy", "groovy"),
        (".gradle", "groovy"),
        (".jl", "julia"),
        (".ini", "ini"),
        (".cfg", "ini"),
        (".conf", "ini"),
        (".properties", "properties"),
        (".env", "dotenv"),
        (".ada", "ada"),
        (".pas", "pascal"),
        (".f90", "fortran"),
        (".f95", "fortran"),
        (".cob", "cobol"),
        (".asm", "asm"),
        (".s", "asm"),
        (".c++", "cpp"),
        (".h++", "cpp"),
        (".ipp", "cpp"),
        (".tpp", "cpp"),
        (".phtml", "php"),
        (".php3", "php"),
        (".php4", "php"),
        (".php5", "php"),
        (".php7", "php"),
        (".phps", "php"),
        (".adoc", "asciidoc"),
        (".editorconfig", "editorconfig"),
        (".tex", "latex"),
        (".latex", "latex"),
        (".rst", "rst"),
        (".txt", "text"),
        (".log", "text"),
        (".diff", "diff"),
        (".patch", "diff"),
        (".vue", "vue"),
        (".svelte", "svelte"),
        (".sty", "latex"),
        (".cls", "latex"),
        (".bst", "latex"),
        (".dtx", "latex"),
        (".bib", "bibtex"),
        (".nix", "nix"),
        (".prisma", "prisma"),
        (".bzl", "bazel"),
        (".j2", "jinja"),
        (".jinja", "jinja"),
        (".jinja2", "jinja"),
    ];

    let mut map = FxHashMap::with_capacity_and_hasher(entries.len(), Default::default());
    for &(ext, lang) in entries {
        map.insert(ext, lang);
    }
    map
});

pub static FILENAME_TO_LANGUAGE: Lazy<FxHashMap<&'static str, &'static str>> = Lazy::new(|| {
    let entries: &[(&str, &str)] = &[
        ("makefile", "makefile"),
        ("gnumakefile", "makefile"),
        ("dockerfile", "dockerfile"),
        ("containerfile", "dockerfile"),
        ("vagrantfile", "ruby"),
        ("gemfile", "ruby"),
        ("rakefile", "ruby"),
        ("guardfile", "ruby"),
        ("brewfile", "ruby"),
        ("podfile", "ruby"),
        ("cmakelists.txt", "cmake"),
        ("justfile", "just"),
        (".bashrc", "bash"),
        (".bash_profile", "bash"),
        (".bash_aliases", "bash"),
        (".zshrc", "zsh"),
        (".zshenv", "zsh"),
        (".zprofile", "zsh"),
        (".profile", "bash"),
        (".gitconfig", "gitconfig"),
        (".gitattributes", "gitattributes"),
        (".gitignore", "gitignore"),
        (".dockerignore", "gitignore"),
        (".diffctxignore", "gitignore"),
        (".npmrc", "ini"),
        (".yarnrc", "yaml"),
        (".prettierrc", "json"),
        (".eslintrc", "json"),
        ("package.json", "json"),
        ("tsconfig.json", "json"),
        ("composer.json", "json"),
        ("cargo.toml", "toml"),
        ("pyproject.toml", "toml"),
        ("go.mod", "gomod"),
        ("go.sum", "gosum"),
        ("requirements.txt", "text"),
        ("pipfile", "toml"),
        ("procfile", "text"),
        ("jenkinsfile", "groovy"),
        ("build", "bazel"),
        ("build.bazel", "bazel"),
        ("workspace", "bazel"),
        ("workspace.bazel", "bazel"),
        ("flake.lock", "json"),
    ];

    let mut map = FxHashMap::with_capacity_and_hasher(entries.len(), Default::default());
    for &(name, lang) in entries {
        map.insert(name, lang);
    }
    map
});

pub fn get_language_for_file(path: &str) -> Option<&'static str> {
    let p = Path::new(path);

    if let Some(name) = p.file_name() {
        let name_lower = name.to_string_lossy().to_lowercase();
        if let Some(&lang) = FILENAME_TO_LANGUAGE.get(name_lower.as_str()) {
            return Some(lang);
        }
    }

    if let Some(ext) = p.extension() {
        let ext_lower = format!(".{}", ext.to_string_lossy().to_lowercase());
        if let Some(&lang) = EXTENSION_TO_LANGUAGE.get(ext_lower.as_str()) {
            return Some(lang);
        }
    }

    if let Some(name) = p.file_name() {
        let name_lower = name.to_string_lossy().to_lowercase();
        if name_lower.starts_with("dockerfile") {
            return Some("dockerfile");
        }
    }

    None
}