Skip to content

Commit e7f2b53

Browse files
fix(Mountain): align provider selector format and add language inference
Fix the provider registration and lookup chain: - Update CocoonService.RegisterProvider to use canonical selector format [{ "language": ... }] matching ProviderLookup's expectations - Change SideCarIdentifier to always route to "cocoon-main" instead of extension_id, enabling FeatureMethods to invoke providers correctly - Add language inference fallback in ProviderLookup: if document not in open state, derive language from file extension (.rs→rust, .ts→typescript, etc.) This resolves provider lookup failures for language features like hover, completion, etc.
1 parent ee41baf commit e7f2b53

2 files changed

Lines changed: 80 additions & 15 deletions

File tree

Source/Environment/LanguageFeatureProvider/ProviderLookup.rs

Lines changed: 74 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,83 @@ pub(super) async fn get_matching_provider(
2929
.lock()
3030
.map_err(MapApplicationStateLockErrorToCommonError)?;
3131

32-
if let Some(document) = open_documents.get(document_uri.as_str()) {
33-
for provider in providers.values() {
34-
if provider.ProviderType == feature_type {
35-
if let Some(selector_array) = provider.Selector.as_array() {
36-
for selector in selector_array {
37-
if let Some(language) = selector.get("language").and_then(|l| l.as_str()) {
38-
if language == document.LanguageIdentifier {
39-
return Ok(Some(provider.clone()));
40-
}
41-
}
42-
}
32+
// Derive language: prefer DocumentState record, fall back to URI extension.
33+
let LanguageId:String = if let Some(Document) = open_documents.get(document_uri.as_str()) {
34+
Document.LanguageIdentifier.clone()
35+
} else {
36+
// Document not yet opened via model:open — infer from file extension.
37+
document_uri
38+
.path()
39+
.split('.')
40+
.next_back()
41+
.map(|Ext| match Ext {
42+
"rs" => "rust",
43+
"ts" | "tsx" => "typescript",
44+
"js" | "jsx" | "mjs" | "cjs" => "javascript",
45+
"json" | "jsonc" => "json",
46+
"toml" => "toml",
47+
"yaml" | "yml" => "yaml",
48+
"md" => "markdown",
49+
"py" => "python",
50+
"go" => "go",
51+
"c" | "h" => "c",
52+
"cpp" | "cc" | "cxx" | "hpp" => "cpp",
53+
Other => Other,
54+
})
55+
.unwrap_or("plaintext")
56+
.to_string()
57+
};
58+
59+
for Provider in providers.values() {
60+
if Provider.ProviderType != feature_type {
61+
continue;
62+
}
63+
// Selector shapes (all stored as JSON from CocoonService.RegisterProvider):
64+
// Canonical: [{ "language": "typescript" }]
65+
// Wildcard: [{ "language": "*" }]
66+
// Legacy obj: { "language": ["typescript"] }
67+
// Plain str: "*"
68+
let Matched = if let Some(SelectorArray) = Provider.Selector.as_array() {
69+
SelectorArray.iter().any(|S| {
70+
match S.get("language") {
71+
Some(L) if L.as_str() == Some(&LanguageId) => true,
72+
Some(L) if L.as_str() == Some("*") => true,
73+
Some(L) => L
74+
.as_array()
75+
.map(|Arr| {
76+
Arr.iter().any(|Item| {
77+
Item.as_str() == Some(&LanguageId) || Item.as_str() == Some("*")
78+
})
79+
})
80+
.unwrap_or(false),
81+
None => false,
4382
}
44-
}
83+
})
84+
} else if let Some(LangValue) = Provider.Selector.get("language") {
85+
LangValue.as_str() == Some(&LanguageId)
86+
|| LangValue.as_str() == Some("*")
87+
|| LangValue
88+
.as_array()
89+
.map(|Arr| {
90+
Arr.iter().any(|Item| {
91+
Item.as_str() == Some(&LanguageId) || Item.as_str() == Some("*")
92+
})
93+
})
94+
.unwrap_or(false)
95+
} else if let Some(LangStr) = Provider.Selector.as_str() {
96+
LangStr == &LanguageId || LangStr == "*"
97+
} else {
98+
false
99+
};
100+
101+
if Matched {
102+
return Ok(Some(Provider.clone()));
45103
}
46104
}
47105

48-
warn!("No provider found for {:?} on document {}", feature_type, document_uri);
106+
warn!(
107+
"[ProviderLookup] No {:?} provider for language '{}' (uri={})",
108+
feature_type, LanguageId, document_uri
109+
);
49110
Ok(None)
50111
}

Source/RPC/CocoonService.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,11 +320,15 @@ impl CocoonServiceImpl {
320320
/// - `language_selector`: Language scope (e.g. "typescript")
321321
/// - `extension_id`: Extension that registered this provider
322322
fn RegisterProvider(&self, handle:u32, provider_type:ProviderType, language_selector:&str, extension_id:&str) {
323+
// SideCarIdentifier = "cocoon-main" so FeatureMethods::invoke_provider can
324+
// route back via Vine::Client::SendRequestToSideCar("cocoon-main", ...).
325+
// Selector stored as array so ProviderLookup::get_matching_provider's
326+
// `.as_array()` call finds the language entry: [{ "language": "typescript" }].
323327
let dto = ProviderRegistrationDTO {
324328
Handle:handle,
325329
ProviderType:provider_type,
326-
Selector:json!({ "language": [language_selector] }),
327-
SideCarIdentifier:extension_id.to_string(),
330+
Selector:json!([{ "language": language_selector }]),
331+
SideCarIdentifier:"cocoon-main".to_string(),
328332
ExtensionIdentifier:json!(extension_id),
329333
Options:None,
330334
};

0 commit comments

Comments
 (0)