Madhusudhanan Balasubramanian

17 papers Journal 14Unranked 3
YearRankTypeTitle / Venue / Authors
2023 conf
3DIA
Hanieh Shabanian, Madhusudhanan Balasubramanian
2023 J jnl
Neural Process. Lett.
Ali Salehi, Madhusudhanan Balasubramanian
2023 J jnl
Neurocomputing
Ali Salehi, Madhusudhanan Balasubramanian
2023 J jnl
CoRR
Fisseha Admasu Ferede, Madhusudhanan Balasubramanian
2023 J jnl
Neurocomputing
Fisseha Admasu Ferede, Madhusudhanan Balasubramanian
2022 J jnl
CoRR
Hanieh Shabanian, Madhusudhanan Balasubramanian
2021 conf
SD&A
Hanieh Shabanian, Madhusudhanan Balasubramanian
2021 J jnl
CoRR
Hanieh Shabanian, Madhusudhanan Balasubramanian
2021 J jnl
CoRR
Ali Salehi, Madhusudhanan Balasubramanian
2021 J jnl
CoRR
Ali Salehi, Madhusudhanan Balasubramanian
2015 J jnl
Artif. Intell. Medicine
Akram Belghith, Christopher Bowd, Felipe A. Medeiros, Madhusudhanan Balasubramanian, Robert N. Weinreb, Linda M. Zangwill
2014 J jnl
Comput. Medical Imaging Graph.
Akram Belghith, Madhusudhanan Balasubramanian, Christopher Bowd, Robert N. Weinreb, Linda M. Zangwill
2014 J jnl
IEEE Trans. Biomed. Eng.
Siamak Yousefi, Michael H. Goldbaum, Madhusudhanan Balasubramanian, Tzyy-Ping Jung, Robert N. Weinreb, Felipe A. Medeiros, Linda M. Zangwill, Jeffrey M. Liebmann, Christopher A. Girkin, Christopher Bowd
2014 J jnl
IEEE Trans. Biomed. Eng.
Siamak Yousefi, Michael H. Goldbaum, Madhusudhanan Balasubramanian, Felipe A. Medeiros, Linda M. Zangwill, Jeffrey M. Liebmann, Christopher A. Girkin, Robert N. Weinreb, Christopher Bowd
2013 conf
ISBI
Akram Belghith, Madhusudhanan Balasubramanian, Christopher Bowd, Robert N. Weinreb, Linda M. Zangwill
2009 J jnl
IEEE Trans. Inf. Technol. Biomed.
Madhusudhanan Balasubramanian, Stanislav Zabic, Christopher Bowd, Hilary W. Thompson, Peter R. Wolenski, S. Sitharama Iyengar, Bijaya B. Karki, Linda M. Zangwill
2007 J jnl
J. Electronic Imaging
Madhusudhanan Balasubramanian, S. Sitharama Iyengar, Roger W. Beuerman, Juan Reynaud, Peter R. Wolenski
src/highlight.rs
← Index src/highlight.rs rust
use once_cell::sync::Lazy;
use regex::Regex;
use std::collections::HashMap;

/// Highlighting rules for a "family" of languages.
struct LangRule {
    line_comments: &'static [&'static str],
    block_comments: &'static [(&'static str, &'static str)],
    /// If true, enables strings delimited by " and '
    strings: bool,
    /// If true, also enables triple-quoted strings (Python)
    triple_quote: bool,
    /// If true, also enables backtick template strings (JS/TS)
    backtick: bool,
    /// If true, keywords are matched case-insensitively (e.g. SQL)
    case_insensitive_keywords: bool,
    keywords: &'static [&'static str],
}

const EMPTY_KW: &[&str] = &[];

fn rule_for(lang: &str) -> LangRule {
    match lang {
        "rust" => LangRule {
            line_comments: &["//"],
            block_comments: &[("/*", "*/")],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: &[
                "as", "break", "const", "continue", "crate", "dyn", "else", "enum", "extern",
                "false", "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod", "move",
                "mut", "pub", "ref", "return", "self", "Self", "static", "struct", "super",
                "trait", "true", "type", "unsafe", "use", "where", "while", "async", "await",
                "union",
            ],
        },
        "python" => LangRule {
            line_comments: &["#"],
            block_comments: &[],
            strings: true,
            triple_quote: true,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: &[
                "False", "None", "True", "and", "as", "assert", "async", "await", "break",
                "class", "continue", "def", "del", "elif", "else", "except", "finally", "for",
                "from", "global", "if", "import", "in", "is", "lambda", "nonlocal", "not", "or",
                "pass", "raise", "return", "try", "while", "with", "yield", "self",
            ],
        },
        "javascript" | "typescript" => LangRule {
            line_comments: &["//"],
            block_comments: &[("/*", "*/")],
            strings: true,
            triple_quote: false,
            backtick: true,
            case_insensitive_keywords: false,
            keywords: &[
                "break", "case", "catch", "class", "const", "continue", "debugger", "default",
                "delete", "do", "else", "export", "extends", "finally", "for", "function", "if",
                "import", "in", "instanceof", "new", "return", "super", "switch", "this", "throw",
                "try", "typeof", "var", "void", "while", "with", "yield", "let", "static",
                "async", "await", "of", "null", "true", "false", "undefined", "interface",
                "type", "enum", "implements", "namespace", "readonly", "public", "private",
                "protected", "abstract", "as", "declare", "from", "get", "set",
            ],
        },
        "java" => LangRule {
            line_comments: &["//"],
            block_comments: &[("/*", "*/")],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: &[
                "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char",
                "class", "const", "continue", "default", "do", "double", "else", "enum",
                "extends", "final", "finally", "float", "for", "goto", "if", "implements",
                "import", "instanceof", "int", "interface", "long", "native", "new", "package",
                "private", "protected", "public", "return", "short", "static", "strictfp",
                "super", "switch", "synchronized", "this", "throw", "throws", "transient", "try",
                "void", "volatile", "while", "true", "false", "null", "var", "record", "yield",
                "sealed", "permits",
            ],
        },
        "c" => LangRule {
            line_comments: &["//"],
            block_comments: &[("/*", "*/")],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: &[
                "auto", "break", "case", "char", "const", "continue", "default", "do", "double",
                "else", "enum", "extern", "float", "for", "goto", "if", "inline", "int", "long",
                "register", "restrict", "return", "short", "signed", "sizeof", "static",
                "struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while",
            ],
        },
        "cpp" => LangRule {
            line_comments: &["//"],
            block_comments: &[("/*", "*/")],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: &[
                "auto", "break", "case", "char", "const", "continue", "default", "do", "double",
                "else", "enum", "extern", "float", "for", "goto", "if", "inline", "int", "long",
                "register", "return", "short", "signed", "sizeof", "static", "struct", "switch",
                "typedef", "union", "unsigned", "void", "volatile", "while", "class",
                "namespace", "template", "typename", "public", "private", "protected", "virtual",
                "friend", "this", "new", "delete", "try", "catch", "throw", "using", "operator",
                "explicit", "mutable", "bool", "true", "false", "nullptr", "constexpr",
                "decltype", "noexcept", "override", "final",
            ],
        },
        "csharp" => LangRule {
            line_comments: &["//"],
            block_comments: &[("/*", "*/")],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: &[
                "abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char",
                "checked", "class", "const", "continue", "decimal", "default", "delegate", "do",
                "double", "else", "enum", "event", "explicit", "extern", "false", "finally",
                "fixed", "float", "for", "foreach", "goto", "if", "implicit", "in", "int",
                "interface", "internal", "is", "lock", "long", "namespace", "new", "null",
                "object", "operator", "out", "override", "params", "private", "protected",
                "public", "readonly", "ref", "return", "sbyte", "sealed", "short", "sizeof",
                "stackalloc", "static", "string", "struct", "switch", "this", "throw", "true",
                "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using",
                "virtual", "void", "volatile", "while", "var", "async", "await", "yield",
                "nameof",
            ],
        },
        "go" => LangRule {
            line_comments: &["//"],
            block_comments: &[("/*", "*/")],
            strings: true,
            triple_quote: false,
            backtick: true,
            case_insensitive_keywords: false,
            keywords: &[
                "break", "case", "chan", "const", "continue", "default", "defer", "else",
                "fallthrough", "for", "func", "go", "goto", "if", "import", "interface", "map",
                "package", "range", "return", "select", "struct", "switch", "type", "var",
                "true", "false", "nil", "iota",
            ],
        },
        "ruby" => LangRule {
            line_comments: &["#"],
            block_comments: &[],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: &[
                "def", "end", "if", "elsif", "else", "unless", "while", "until", "for", "in",
                "do", "begin", "rescue", "ensure", "raise", "class", "module", "self", "nil",
                "true", "false", "and", "or", "not", "then", "yield", "return", "break", "next",
                "redo", "retry", "super", "require", "require_relative", "attr_accessor",
                "attr_reader", "attr_writer", "private", "public", "protected",
            ],
        },
        "php" => LangRule {
            line_comments: &["//", "#"],
            block_comments: &[("/*", "*/")],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: &[
                "abstract", "and", "array", "as", "break", "callable", "case", "catch", "class",
                "clone", "const", "continue", "declare", "default", "do", "echo", "else",
                "elseif", "empty", "extends", "final", "finally", "fn", "for", "foreach",
                "function", "global", "goto", "if", "implements", "include", "include_once",
                "instanceof", "interface", "isset", "list", "match", "namespace", "new", "or",
                "print", "private", "protected", "public", "require", "require_once", "return",
                "static", "switch", "throw", "trait", "try", "unset", "use", "var", "while",
                "xor", "yield", "true", "false", "null", "self", "parent",
            ],
        },
        "bash" => LangRule {
            line_comments: &["#"],
            block_comments: &[],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: &[
                "if", "then", "else", "elif", "fi", "for", "while", "until", "do", "done",
                "case", "esac", "function", "in", "return", "break", "continue", "local",
                "export", "readonly", "shift", "exit", "echo", "source",
            ],
        },
        "sql" => LangRule {
            line_comments: &["--"],
            block_comments: &[("/*", "*/")],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: true,
            keywords: &[
                "select", "from", "where", "insert", "into", "values", "update", "set",
                "delete", "create", "table", "alter", "drop", "join", "inner", "left", "right",
                "outer", "on", "group", "by", "order", "having", "as", "and", "or", "not",
                "null", "is", "in", "like", "limit", "offset", "distinct", "union", "all",
                "exists", "between", "case", "when", "then", "end", "primary", "key", "foreign",
                "references", "default", "index", "view", "trigger", "procedure", "function",
                "begin", "commit", "rollback",
            ],
        },
        "lua" => LangRule {
            line_comments: &["--"],
            block_comments: &[("--[[", "]]")],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: &[
                "and", "break", "do", "else", "elseif", "end", "false", "for", "function", "if",
                "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until",
                "while",
            ],
        },
        "kotlin" => LangRule {
            line_comments: &["//"],
            block_comments: &[("/*", "*/")],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: &[
                "as", "break", "class", "continue", "do", "else", "false", "for", "fun", "if",
                "in", "interface", "is", "null", "object", "package", "return", "super", "this",
                "throw", "true", "try", "typealias", "val", "var", "when", "while", "by",
                "companion", "constructor", "data", "enum", "import", "init", "override",
                "private", "protected", "public", "sealed", "suspend",
            ],
        },
        "swift" => LangRule {
            line_comments: &["//"],
            block_comments: &[("/*", "*/")],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: &[
                "class", "deinit", "enum", "extension", "func", "import", "init", "internal",
                "let", "operator", "private", "protocol", "public", "static", "struct",
                "subscript", "typealias", "var", "break", "case", "continue", "default", "do",
                "else", "fallthrough", "if", "in", "for", "return", "switch", "where", "while",
                "as", "false", "true", "guard", "nil", "self", "super", "throw", "try", "async",
                "await",
            ],
        },
        "css" | "scss" | "less" => LangRule {
            line_comments: &[],
            block_comments: &[("/*", "*/")],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: EMPTY_KW,
        },
        "xml" => LangRule {
            line_comments: &[],
            block_comments: &[("<!--", "-->")],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: EMPTY_KW,
        },
        "json" => LangRule {
            line_comments: &[],
            block_comments: &[],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: &["true", "false", "null"],
        },
        "yaml" => LangRule {
            line_comments: &["#"],
            block_comments: &[],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: &["true", "false", "null", "yes", "no"],
        },
        "ini" => LangRule {
            line_comments: &["#", ";"],
            block_comments: &[],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: EMPTY_KW,
        },
        "dockerfile" => LangRule {
            line_comments: &["#"],
            block_comments: &[],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: true,
            keywords: &[
                "from", "run", "cmd", "label", "expose", "env", "add", "copy", "entrypoint",
                "volume", "user", "workdir", "arg", "onbuild", "stopsignal", "healthcheck",
                "shell",
            ],
        },
        "makefile" => LangRule {
            line_comments: &["#"],
            block_comments: &[],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: EMPTY_KW,
        },
        "graphql" => LangRule {
            line_comments: &["#"],
            block_comments: &[],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: &["query", "mutation", "subscription", "type", "input", "enum", "interface", "fragment", "on"],
        },
        // Language families without detailed specific rules: still
        // highlight comments/strings/numbers generically.
        "perl" => LangRule {
            line_comments: &["#"],
            block_comments: &[],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: &["my", "our", "sub", "if", "elsif", "else", "unless", "while", "for", "foreach", "return", "use", "package"],
        },
        "groovy" => LangRule {
            line_comments: &["//"],
            block_comments: &[("/*", "*/")],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: EMPTY_KW,
        },
        "r" => LangRule {
            line_comments: &["#"],
            block_comments: &[],
            strings: true,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: &["function", "if", "else", "for", "while", "repeat", "break", "next", "return", "TRUE", "FALSE", "NULL", "NA"],
        },
        "diff" => LangRule {
            line_comments: &[],
            block_comments: &[],
            strings: false,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: EMPTY_KW,
        },
        // markdown, plaintext, cmake, vbnet, dos, powershell, etc:
        // no specific highlighting, plain but formatted text.
        _ => LangRule {
            line_comments: &[],
            block_comments: &[],
            strings: false,
            triple_quote: false,
            backtick: false,
            case_insensitive_keywords: false,
            keywords: EMPTY_KW,
        },
    }
}

/// Cache of compiled regexes (one per language actually encountered).
static REGEX_CACHE: Lazy<std::sync::Mutex<HashMap<String, Option<Regex>>>> =
    Lazy::new(|| std::sync::Mutex::new(HashMap::new()));

fn build_regex(rule: &LangRule) -> Option<Regex> {
    let mut comment_alts: Vec<String> = Vec::new();
    for (start, end) in rule.block_comments {
        comment_alts.push(format!(
            "(?s:{}.*?{})",
            regex::escape(start),
            regex::escape(end)
        ));
    }
    for prefix in rule.line_comments {
        comment_alts.push(format!("{}[^\n]*", regex::escape(prefix)));
    }

    let mut string_alts: Vec<String> = Vec::new();
    if rule.triple_quote {
        string_alts.push(r#"(?s:"""(?:[^"\\]|\\.)*?""")"#.to_string());
        string_alts.push(r#"(?s:'''(?:[^'\\]|\\.)*?''')"#.to_string());
    }
    if rule.strings {
        string_alts.push(r#""(?:[^"\\\n]|\\.)*""#.to_string());
        string_alts.push(r#"'(?:[^'\\\n]|\\.)*'"#.to_string());
    }
    if rule.backtick {
        string_alts.push(r"(?s:`(?:[^`\\]|\\.)*`)".to_string());
    }

    let number_pat = r"\b0[xX][0-9a-fA-F]+\b|\b\d+\.\d+(?:[eE][+-]?\d+)?\b|\b\d+\b".to_string();

    let mut keyword_alt = String::new();
    if !rule.keywords.is_empty() {
        let escaped: Vec<String> = rule.keywords.iter().map(|k| regex::escape(k)).collect();
        let joined = escaped.join("|");
        keyword_alt = if rule.case_insensitive_keywords {
            format!("(?i:\\b(?:{})\\b)", joined)
        } else {
            format!("\\b(?:{})\\b", joined)
        };
    }

    let mut parts: Vec<String> = Vec::new();
    if !comment_alts.is_empty() {
        parts.push(format!("(?P<comment>{})", comment_alts.join("|")));
    }
    if !string_alts.is_empty() {
        parts.push(format!("(?P<string>{})", string_alts.join("|")));
    }
    parts.push(format!("(?P<number>{})", number_pat));
    if !keyword_alt.is_empty() {
        parts.push(format!("(?P<keyword>{})", keyword_alt));
    }

    if parts.is_empty() {
        return None;
    }

    Regex::new(&parts.join("|")).ok()
}

fn escape_html(s: &str) -> String {
    let mut out = String::with_capacity(s.len());
    for c in s.chars() {
        match c {
            '&' => out.push_str("&amp;"),
            '<' => out.push_str("&lt;"),
            '>' => out.push_str("&gt;"),
            '"' => out.push_str("&quot;"),
            '\'' => out.push_str("&#39;"),
            _ => out.push(c),
        }
    }
    out
}

/// Highlights the source code and returns HTML that's already escaped
/// and ready to insert inside a `<pre><code>...</code></pre>`.
/// No JavaScript involved: all the work happens here, in Rust,
/// at file-generation time.
pub fn highlight_code(content: &str, lang: &str) -> String {
    let rule = rule_for(lang);

    let regex_opt = {
        let mut cache = REGEX_CACHE.lock().unwrap();
        cache
            .entry(lang.to_string())
            .or_insert_with(|| build_regex(&rule))
            .clone()
    };

    let Some(re) = regex_opt else {
        return escape_html(content);
    };

    let mut out = String::with_capacity(content.len() + content.len() / 4);
    let mut last = 0;

    for caps in re.captures_iter(content) {
        let m = caps.get(0).unwrap();
        out.push_str(&escape_html(&content[last..m.start()]));

        let class = if caps.name("comment").is_some() {
            "c"
        } else if caps.name("string").is_some() {
            "s"
        } else if caps.name("number").is_some() {
            "n"
        } else if caps.name("keyword").is_some() {
            "k"
        } else {
            ""
        };

        let escaped_match = escape_html(m.as_str());
        if class.is_empty() {
            out.push_str(&escaped_match);
        } else {
            out.push_str(&format!("<span class=\"{}\">{}</span>", class, escaped_match));
        }
        last = m.end();
    }
    out.push_str(&escape_html(&content[last..]));
    out
}