#![allow(dead_code)]
use std::collections::{HashMap, HashSet};
use std::path::Path;
use super::installed_plugins_manager::is_plugin_installed;
use super::marketplace_manager::{get_marketplace, load_known_marketplaces_config_safe};
use super::schemas::allowed_official_marketplace_names;
const MAX_IGNORED_COUNT: u32 = 5;
pub struct LspPluginRecommendation {
pub plugin_id: String,
pub plugin_name: String,
pub marketplace_name: String,
pub description: Option<String>,
pub is_official: bool,
pub extensions: Vec<String>,
pub command: String,
}
struct LspInfo {
extensions: HashSet<String>,
command: String,
}
fn extract_lsp_info_from_manifest(lsp_servers: &serde_json::Value) -> Option<LspInfo> {
if lsp_servers.is_string() {
return None;
}
let mut extensions = HashSet::new();
let mut command: Option<String> = None;
if let Some(obj) = lsp_servers.as_object() {
for (_server_name, config) in obj {
if let Some(cmd) = config.get("command").and_then(|c| c.as_str()) {
if command.is_none() {
command = Some(cmd.to_string());
}
}
if let Some(ext_mapping) = config
.get("extensionToLanguage")
.and_then(|e| e.as_object())
{
for ext in ext_mapping.keys() {
extensions.insert(ext.to_lowercase());
}
}
}
}
if let Some(cmd) = command {
if !extensions.is_empty() {
return Some(LspInfo {
extensions,
command: cmd,
});
}
}
None
}
async fn get_lsp_plugins_from_marketplaces() -> HashMap<String, LspPluginInfo> {
let mut result = HashMap::new();
match load_known_marketplaces_config_safe().await {
Ok(config) => {
for marketplace_name in config.keys() {
match get_marketplace(marketplace_name).await {
Ok(marketplace) => {
let is_official = is_official_marketplace(marketplace_name);
for entry in &marketplace.plugins {
if let Some(ref lsp_servers) = entry.lsp_servers {
if let Some(lsp_info) = extract_lsp_info_from_manifest(lsp_servers)
{
let plugin_id = format!("{}@{}", entry.name, marketplace_name);
result.insert(
plugin_id,
LspPluginInfo {
entry: entry.clone(),
marketplace_name: marketplace_name.clone(),
extensions: lsp_info.extensions,
command: lsp_info.command,
is_official,
},
);
}
}
}
}
Err(e) => {
log::debug!(
"[lsp_recommendation] Failed to load marketplace {}: {}",
marketplace_name,
e
);
}
}
}
}
Err(e) => {
log::debug!(
"[lsp_recommendation] Failed to load marketplaces config: {}",
e
);
}
}
result
}
struct LspPluginInfo {
entry: super::types::PluginMarketplaceEntry,
marketplace_name: String,
extensions: HashSet<String>,
command: String,
is_official: bool,
}
fn is_official_marketplace(name: &str) -> bool {
allowed_official_marketplace_names()
.iter()
.any(|&s| s == name.to_lowercase())
}
pub async fn get_matching_lsp_plugins(file_path: &str) -> Vec<LspPluginRecommendation> {
if is_lsp_recommendations_disabled() {
return Vec::new();
}
let ext = Path::new(file_path)
.extension()
.map(|e| e.to_string_lossy().to_lowercase())
.unwrap_or_default();
if ext.is_empty() {
return Vec::new();
}
let all_lsp_plugins = get_lsp_plugins_from_marketplaces().await;
let mut matching: Vec<LspPluginRecommendation> = Vec::new();
for (plugin_id, info) in &all_lsp_plugins {
if !info.extensions.contains(&ext) {
continue;
}
if is_plugin_installed(plugin_id) {
continue;
}
matching.push(LspPluginRecommendation {
plugin_id: plugin_id.clone(),
plugin_name: info.entry.name.clone(),
marketplace_name: info.marketplace_name.clone(),
description: info.entry.description.clone(),
is_official: info.is_official,
extensions: info.extensions.iter().cloned().collect(),
command: info.command.clone(),
});
}
matching.sort_by(|a, b| match (a.is_official, b.is_official) {
(true, false) => std::cmp::Ordering::Less,
(false, true) => std::cmp::Ordering::Greater,
_ => std::cmp::Ordering::Equal,
});
matching
}
pub fn add_to_never_suggest(_plugin_id: &str) {
}
pub fn _increment_ignored_count() {
}
pub fn is_lsp_recommendations_disabled() -> bool {
false
}
pub fn _reset_ignored_count() {
}