Repository: helix-editor/helix
File: xtask/src/docgen.rs (Rust)

Code before:

    let mut row = Vec::new();
    for lang in langs {
        let lc = config
            .language
            .iter()
            .find(|l| l.language_id == lang)
            .unwrap(); // lang comes from config
        row.push(lc.language_id.clone());

        for (_feat, support_list) in &ts_features_to_langs {
            row.push(
                if support_list.contains(&lang) {
                    "✓"
                } else {
                    ""
                }
                .to_owned(),
            );
        }
        let mut v: Vec<String> = lc.language_servers
                            .iter()
                            .filter_map(|ls| config.language_server.get(&ls.name))
                            .map(|s| md_mono(&s.command.clone()))
                            .collect::<Vec<_>>();
        v.sort();
        v.dedup();
        row.push(v.as_slice().join(", "));

        md.push_str(&md_table_row(&row));
        row.clear();
    }

    Ok(md)
}

pub fn write(filename: &str, data: &str) {
    let error = format!("Could not write to {}", filename);
    let path = path::book_gen().join(filename);
    fs::write(path, data).expect(&error);
}


Review comment (refactor):
I would prefer that we keep the order that we have in the `languages.toml` file for this generated doc since definition order is somewhat important (for example code actions and symbol pickers are ordered by them).

We can use a hash set that we insert names into as we go so that we keep the order and remove duplicates:

```rust
let mut seen_commands = HashSet::new();
let mut commands = String::new();
for ls_config in lc
    .language_servers
    .iter()
    .filter_map(|ls| config.language_server.get(&ls.name))
{
    let command = &ls_config.command;
    if !seen_commands.insert(command) {
        continue;
    }

    if !commands.is_empty() {
        commands.push_str(", ");
    }

    commands.push_str(&md_mono(command));
}
row.push(commands);
```

Code after:
    }

    let mut row = Vec::new();
    for lang in langs {
        let lc = config
            .language
            .iter()
            .find(|l| l.language_id == lang)
            .unwrap(); // lang comes from config
        row.push(lc.language_id.clone());

        for (_feat, support_list) in &ts_features_to_langs {
            row.push(
                if support_list.contains(&lang) {
                    "✓"
                } else {
                    ""
                }
                .to_owned(),
            );
        }
        let mut seen_commands = HashSet::new();
        let mut commands = String::new();
        for ls_config in lc
            .language_servers
            .iter()
            .filter_map(|ls| config.language_server.get(&ls.name))
        {
            let command = &ls_config.command;
            if !seen_commands.insert(command) {
                continue;
            }

            if !commands.is_empty() {
                commands.push_str(", ");
            }

            commands.push_str(&md_mono(command));
        }
        row.push(commands);

        md.push_str(&md_table_row(&row));
        row.clear();
    }

    Ok(md)
}

pub fn write(filename: &str, data: &str) {
    let error = format!("Could not write to {}", filename);
    let path = path::book_gen().join(filename);