<commit_before>#![allow(clippy::manual_map)]
use super::{gi_docgen, LocationInObject};
use crate::{
    analysis::functions::Info,
    library::{FunctionKind, TypeId},
    nameutil, Env,
};
use log::{info, warn};
use once_cell::sync::Lazy;
use regex::{Captures, Regex};

const LANGUAGE_SEP_BEGIN: &str = "<!-- language=\"";
const LANGUAGE_SEP_END: &str = "\" -->";
const LANGUAGE_BLOCK_BEGIN: &str = "|[";
const LANGUAGE_BLOCK_END: &str = "\n]|";

// A list of function names that are ignored when warning about a "not found function"
const IGNORE_C_WARNING_FUNCS: [&str; 6] = [
    "g_object_unref",
    "g_object_ref",
    "g_free",
    "g_list_free",
    "g_strfreev",
    "printf",
];

pub fn reformat_doc(
    input: &str,
    env: &Env,
    in_type: Option<(&TypeId, Option<LocationInObject>)>,
) -> String {
    code_blocks_transformation(input, env, in_type)
}

fn try_split<'a>(src: &'a str, needle: &str) -> (&'a str, Option<&'a str>) {
    match src.find(needle) {
        Some(pos) => (&src[..pos], Some(&src[pos + needle.len()..])),
        None => (src, None),
    }
}

fn code_blocks_transformation(
    mut input: &str,
    env: &Env,
    in_type: Option<(&TypeId, Option<LocationInObject>)>,
) -> String {
    let mut out = String::with_capacity(input.len());

    loop {
        input = match try_split(input, LANGUAGE_BLOCK_BEGIN) {
            (before, Some(after)) => {
                out.push_str(&format(before, env, in_type));
                if let (before, Some(after)) =
                    try_split(get_language(after, &mut out), LANGUAGE_BLOCK_END)
                {
                    out.push_str(before);
                    out.push_str("\n```");
                    after
                } else {
                    after
                }
            }
            (before, None) => {
                out.push_str(&format(before, env, in_type));
                return out;
            }
        };
    }
}

fn get_language<'a>(entry: &'a str, out: &mut String) -> &'a str {
    if let (_, Some(after)) = try_split(entry, LANGUAGE_SEP_BEGIN) {
        if let (before, Some(after)) = try_split(after, LANGUAGE_SEP_END) {
            if !["text", "rust"].contains(&before) {
                out.push_str(&format!("\n\n**⚠️ The following code is in {} ⚠️**", before));
            }
            out.push_str(&format!("\n\n```{}", before));
            return after;
        }
    }
    out.push_str("\n```text");
    entry
}

// try to get the language if any is defined or fallback to text
fn get_markdown_language(input: &str) -> (&str, &str) {
    let (lang, after) = if let Some((lang, after)) = input.split_once("\n") {
        let lang = if lang.is_empty() { None } else { Some(lang) };
        (lang, after)
    } else {
        (None, input)
    };
    (lang.unwrap_or("text"), after)
}

// Re-format codeblocks & replaces the C types and GI-docgen with proper links
fn format(
    mut input: &str,
    env: &Env,
    in_type: Option<(&TypeId, Option<LocationInObject>)>,
) -> String {
    let mut ret = String::with_capacity(input.len());
    loop {
        input = match try_split(input, "```") {
            (before, Some(after)) => {
                // if we are inside a codeblock
                ret.push_str(&replace_symbols(before, env, in_type));

                let (lang, after) = get_markdown_language(after);
                if !["text", "rust"].contains(&lang) && after.lines().count() > 1 {
                    ret.push_str(&format!("**⚠️ The following code is in {0} ⚠️**\n\n", lang));
                }
                ret.push_str(&format!("```{}\n", lang));

                if let (before, Some(after)) = try_split(after, "```") {
                    ret.push_str(before);
                    ret.push_str("```");
                    after
                } else {
                    after
                }
            }
            (before, None) => {
                ret.push_str(&replace_symbols(before, env, in_type));
                return ret;
            }
        }
    }
}

fn replace_symbols(
    input: &str,
    env: &Env,
    in_type: Option<(&TypeId, Option<LocationInObject>)>,
) -> String {
    // We run gi_docgen first because it's super picky about the types it replaces
    let out = gi_docgen::replace_c_types(input, env, in_type);
    let out = replace_c_types(&out, env, in_type);
    // this has to be done after gi_docgen replaced the various types it knows as it uses `@` in it's linking format
    PARAM_SYMBOL<commit_msg>docs: disable language warning for various languages<commit_after>#![allow(clippy::manual_map)]
use super::{gi_docgen, LocationInObject};
use crate::{
    analysis::functions::Info,
    library::{FunctionKind, TypeId},
    nameutil, Env,
};
use log::{info, warn};
use once_cell::sync::Lazy;
use regex::{Captures, Regex};

const LANGUAGE_SEP_BEGIN: &str = "<!-- language=\"";
const LANGUAGE_SEP_END: &str = "\" -->";
const LANGUAGE_BLOCK_BEGIN: &str = "|[";
const LANGUAGE_BLOCK_END: &str = "\n]|";

// A list of function names that are ignored when warning about a "not found function"
const IGNORE_C_WARNING_FUNCS: [&str; 6] = [
    "g_object_unref",
    "g_object_ref",
    "g_free",
    "g_list_free",
    "g_strfreev",
    "printf",
];

pub fn reformat_doc(
    input: &str,
    env: &Env,
    in_type: Option<(&TypeId, Option<LocationInObject>)>,
) -> String {
    code_blocks_transformation(input, env, in_type)
}

fn try_split<'a>(src: &'a str, needle: &str) -> (&'a str, Option<&'a str>) {
    match src.find(needle) {
        Some(pos) => (&src[..pos], Some(&src[pos + needle.len()..])),
        None => (src, None),
    }
}

fn code_blocks_transformation(
    mut input: &str,
    env: &Env,
    in_type: Option<(&TypeId, Option<LocationInObject>)>,
) -> String {
    let mut out = String::with_capacity(input.len());

    loop {
        input = match try_split(input, LANGUAGE_BLOCK_BEGIN) {
            (before, Some(after)) => {
                out.push_str(&format(before, env, in_type));
                if let (before, Some(after)) =
                    try_split(get_language(after, &mut out), LANGUAGE_BLOCK_END)
                {
                    out.push_str(before);
                    out.push_str("\n```");
                    after
                } else {
                    after
                }
            }
            (before, None) => {
                out.push_str(&format(before, env, in_type));
                return out;
            }
        };
    }
}

fn get_language<'a>(entry: &'a str, out: &mut String) -> &'a str {
    if let (_, Some(after)) = try_split(entry, LANGUAGE_SEP_BEGIN) {
        if let (before, Some(after)) = try_split(after, LANGUAGE_SEP_END) {
            if !["text", "rust"].contains(&before) {
                out.push_str(&format!("\n\n**⚠️ The following code is in {} ⚠️**", before));
            }
            out.push_str(&format!("\n\n```{}", before));
            return after;
        }
    }
    out.push_str("\n```text");
    entry
}

// try to get the language if any is defined or fallback to text
fn get_markdown_language(input: &str) -> (&str, &str) {
    let (lang, after) = if let Some((lang, after)) = input.split_once("\n") {
        let lang = if lang.is_empty() { None } else { Some(lang) };
        (lang, after)
    } else {
        (None, input)
    };
    (lang.unwrap_or("text"), after)
}

// Re-format codeblocks & replaces the C types and GI-docgen with proper links
fn format(
    mut input: &str,
    env: &Env,
    in_type: Option<(&TypeId, Option<LocationInObject>)>,
) -> String {
    let mut ret = String::with_capacity(input.len());
    loop {
        input = match try_split(input, "```") {
            (before, Some(after)) => {
                // if we are inside a codeblock
                ret.push_str(&replace_symbols(before, env, in_type));

                let (lang, after) = get_markdown_language(after);
                if !["text", "rust", "xml", "css", "json", "html"].contains(&lang)
                    && after.lines().count() > 1
                {
                    ret.push_str(&format!("**⚠️ The following code is in {0} ⚠️**\n\n", lang));
                }
                ret.push_str(&format!("```{}\n", lang));

                if let (before, Some(after)) = try_split(after, "```") {
                    ret.push_str(before);
                    ret.push_str("```");
                    after
                } else {
                    after
                }
            }
            (before, None) => {
                ret.push_str(&replace_symbols(before, env, in_type));
                return ret;
            }
        }
    }
}

fn replace_symbols(
    input: &str,
    env: &Env,
    in_type: Option<(&TypeId, Option<LocationInObject>)>,
) -> String {
    // We run gi_docgen first because it's super picky about the types it replaces
    let out = gi_docgen::replace_c_types(input, env, in_type);
    let out = replace_c_types(&out, env, in_type);
    // this has to be done after gi_docgen replaced the various types it knows as it uses `@` in it's linking format
    PARAM_SYMBOL
{'fraction_non_alphanumeric': 0.1574570521938943, 'fraction_numerical': 0.000656526972316446, 'mean_word_length': 2.1162632117286053, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 1}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2941790', 'n_tokens_mistral': 3020, 'n_tokens_neox': 2949, 'n_words': 699}