Skip to content

Commit 9c51698

Browse files
committed
support custom request gutter detail for intellij
1 parent 9847ca9 commit 9c51698

File tree

8 files changed

+239
-30
lines changed

8 files changed

+239
-30
lines changed

crates/emmylua_code_analysis/src/db_index/type/mod.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,56 @@ impl LuaTypeIndex {
188188
.map(|supers| supers.iter().map(|s| &s.value))
189189
}
190190

191+
/// Get all direct subclasses of a given type
192+
/// Returns a vector of type declarations that directly inherit from the given type
193+
pub fn get_sub_types(&self, decl_id: &LuaTypeDeclId) -> Vec<&LuaTypeDecl> {
194+
let mut sub_types = Vec::new();
195+
196+
// Iterate through all types and check their super types
197+
for (type_id, supers) in &self.supers {
198+
for super_filed in supers {
199+
// Check if this super type references our target type
200+
if let LuaType::Ref(super_id) = &super_filed.value {
201+
if super_id == decl_id {
202+
// Found a subclass
203+
if let Some(sub_decl) = self.full_name_type_map.get(type_id) {
204+
sub_types.push(sub_decl);
205+
}
206+
break; // No need to check other supers of this type
207+
}
208+
}
209+
}
210+
}
211+
212+
sub_types
213+
}
214+
215+
/// Get all subclasses (direct and indirect) of a given type recursively
216+
/// Returns a vector of type declarations in the inheritance hierarchy
217+
pub fn get_all_sub_types(&self, decl_id: &LuaTypeDeclId) -> Vec<&LuaTypeDecl> {
218+
let mut all_sub_types = Vec::new();
219+
let mut visited = HashSet::new();
220+
let mut queue = vec![decl_id.clone()];
221+
222+
while let Some(current_id) = queue.pop() {
223+
if !visited.insert(current_id.clone()) {
224+
continue;
225+
}
226+
227+
// Find direct subclasses of current_id
228+
let direct_subs = self.get_sub_types(&current_id);
229+
for sub_decl in direct_subs {
230+
let sub_id = sub_decl.get_id();
231+
if !visited.contains(&sub_id) {
232+
all_sub_types.push(sub_decl);
233+
queue.push(sub_id);
234+
}
235+
}
236+
}
237+
238+
all_sub_types
239+
}
240+
191241
pub fn get_type_decl(&self, decl_id: &LuaTypeDeclId) -> Option<&LuaTypeDecl> {
192242
self.full_name_type_map.get(decl_id)
193243
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use lsp_types::request::Request;
2+
use serde::{Deserialize, Serialize};
3+
4+
use crate::handlers::emmy_gutter::GutterKind;
5+
6+
#[derive(Debug)]
7+
pub enum EmmyGutterDetailRequest {}
8+
9+
impl Request for EmmyGutterDetailRequest {
10+
type Params = EmmyGutterDetailParams;
11+
type Result = Option<GutterDetailResponse>;
12+
const METHOD: &'static str = "emmy/gutter/detail";
13+
}
14+
15+
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
16+
pub struct EmmyGutterDetailParams {
17+
pub data: String,
18+
}
19+
20+
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
21+
pub struct GutterLocation {
22+
pub uri: String,
23+
pub line: i32,
24+
pub kind: GutterKind,
25+
}
26+
27+
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
28+
pub struct GutterDetailResponse {
29+
pub locations: Vec<GutterLocation>,
30+
}

crates/emmylua_ls/src/handlers/emmy_gutter/emmy_gutter_request.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,12 @@ pub struct EmmyGutterParams {
1515
pub uri: String,
1616
}
1717

18-
// /**
19-
// * Gutter information returned from LSP
20-
// */
21-
// data class GutterInfo(
22-
// val range: Range,
23-
// val kind: GutterKind,
24-
// val detail: String?
25-
// )
26-
2718
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
2819
pub struct GutterInfo {
2920
pub range: Range,
3021
pub kind: GutterKind,
3122
pub detail: Option<String>,
23+
pub data: Option<String>,
3224
}
3325

3426
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
@@ -39,6 +31,7 @@ pub enum GutterKind {
3931
Alias = 2,
4032
Method = 3,
4133
Module = 4,
34+
Override = 5,
4235
}
4336

4437
impl From<GutterKind> for u8 {
@@ -55,6 +48,7 @@ impl From<u8> for GutterKind {
5548
2 => GutterKind::Alias,
5649
3 => GutterKind::Method,
5750
4 => GutterKind::Module,
51+
5 => GutterKind::Override,
5852
_ => GutterKind::Class, // default case
5953
}
6054
}

crates/emmylua_ls/src/handlers/emmy_gutter/mod.rs

Lines changed: 124 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1+
mod emmy_gutter_detail_request;
12
mod emmy_gutter_request;
23

34
use std::str::FromStr;
45

56
use crate::{
67
context::ServerContextSnapshot,
7-
handlers::emmy_gutter::emmy_gutter_request::{EmmyGutterParams, GutterInfo},
8+
handlers::{
9+
emmy_gutter::emmy_gutter_request::{EmmyGutterParams, GutterInfo},
10+
inlay_hint::{get_override_lsp_location, get_super_member_id},
11+
},
812
};
13+
pub use emmy_gutter_detail_request::*;
914
pub use emmy_gutter_request::*;
10-
use emmylua_code_analysis::SemanticModel;
11-
use emmylua_parser::{LuaAstNode, LuaAstToken, LuaDocTag};
15+
use emmylua_code_analysis::{InferGuard, LuaMemberKey, LuaType, SemanticModel};
16+
use emmylua_parser::{LuaAst, LuaAstNode, LuaAstToken, LuaVarExpr};
1217
use lsp_types::Uri;
1318
use tokio_util::sync::CancellationToken;
1419

@@ -29,38 +34,148 @@ fn build_gutter_infos(semantic_model: &SemanticModel) -> Option<Vec<GutterInfo>>
2934
let root = semantic_model.get_root().clone();
3035
let document = semantic_model.get_document();
3136
let mut gutters = Vec::new();
32-
for tag in root.descendants::<LuaDocTag>() {
37+
for tag in root.descendants::<LuaAst>() {
3338
match tag {
34-
LuaDocTag::Alias(alias) => {
35-
let range = alias.get_name_token()?.get_range();
39+
LuaAst::LuaDocTagAlias(alias) => {
40+
let name_token = alias.get_name_token()?;
41+
let range = name_token.get_range();
42+
let name = name_token.get_text();
3643
let lsp_range = document.to_lsp_range(range)?;
3744
gutters.push(GutterInfo {
3845
range: lsp_range,
3946
kind: GutterKind::Alias,
4047
detail: Some("type alias".to_string()),
48+
data: Some(name.to_string()),
4149
});
4250
}
43-
LuaDocTag::Class(class) => {
44-
let range = class.get_name_token()?.get_range();
51+
LuaAst::LuaDocTagClass(class) => {
52+
let name_token = class.get_name_token()?;
53+
let range = name_token.get_range();
54+
let name = name_token.get_text();
4555
let lsp_range = document.to_lsp_range(range)?;
4656
gutters.push(GutterInfo {
4757
range: lsp_range,
4858
kind: GutterKind::Class,
4959
detail: Some("class".to_string()),
60+
data: Some(name.to_string()),
5061
});
5162
}
52-
LuaDocTag::Enum(enm) => {
63+
LuaAst::LuaDocTagEnum(enm) => {
5364
let range = enm.get_name_token()?.get_range();
5465
let lsp_range = document.to_lsp_range(range)?;
5566
gutters.push(GutterInfo {
5667
range: lsp_range,
5768
kind: GutterKind::Enum,
5869
detail: Some("enum".to_string()),
70+
data: None,
5971
});
6072
}
73+
LuaAst::LuaFuncStat(func_stat) => {
74+
build_func_override_gutter_info(semantic_model, &mut gutters, func_stat);
75+
}
6176
_ => {}
6277
}
6378
}
6479

6580
Some(gutters)
6681
}
82+
83+
fn build_func_override_gutter_info(
84+
semantic_model: &SemanticModel,
85+
gutters: &mut Vec<GutterInfo>,
86+
func_stat: emmylua_parser::LuaFuncStat,
87+
) -> Option<()> {
88+
if !semantic_model.get_emmyrc().hint.override_hint {
89+
return Some(());
90+
}
91+
92+
let func_name = func_stat.get_func_name()?;
93+
let func_name_pos = func_name.get_position();
94+
if let LuaVarExpr::IndexExpr(index_expr) = func_name {
95+
let prefix_expr = index_expr.get_prefix_expr()?;
96+
let prefix_type = semantic_model.infer_expr(prefix_expr).ok()?;
97+
if let LuaType::Def(id) = prefix_type {
98+
let supers = semantic_model
99+
.get_db()
100+
.get_type_index()
101+
.get_super_types(&id)?;
102+
103+
let index_key = index_expr.get_index_key()?;
104+
let member_key: LuaMemberKey = semantic_model.get_member_key(&index_key)?;
105+
let guard = InferGuard::new();
106+
for super_type in supers {
107+
if let Some(member_id) =
108+
get_super_member_id(semantic_model, super_type, &member_key, &guard)
109+
{
110+
let member = semantic_model
111+
.get_db()
112+
.get_member_index()
113+
.get_member(&member_id)?;
114+
115+
let document = semantic_model.get_document();
116+
let func_name_lsp_pos = document.to_lsp_position(func_name_pos)?;
117+
118+
let file_id = member.get_file_id();
119+
let syntax_id = member.get_syntax_id();
120+
let lsp_location =
121+
get_override_lsp_location(semantic_model, file_id, syntax_id)?;
122+
let hint = GutterInfo {
123+
range: lsp_types::Range {
124+
start: func_name_lsp_pos,
125+
end: func_name_lsp_pos,
126+
},
127+
kind: GutterKind::Override,
128+
detail: Some("overrides method".to_string()),
129+
data: Some(format!(
130+
"{}#{}#{}",
131+
lsp_location.uri.get_file_path()?.display(),
132+
lsp_location.range.start.line,
133+
0
134+
)),
135+
};
136+
gutters.push(hint);
137+
break;
138+
}
139+
}
140+
}
141+
}
142+
143+
Some(())
144+
}
145+
146+
pub async fn on_emmy_gutter_detail_handler(
147+
context: ServerContextSnapshot,
148+
params: EmmyGutterDetailParams,
149+
_: CancellationToken,
150+
) -> Option<GutterDetailResponse> {
151+
let type_name = params.data;
152+
let analysis = context.analysis().read().await;
153+
let db = &analysis.compilation.get_db();
154+
let type_index = db.get_type_index();
155+
156+
// Find the type declaration
157+
let type_id = emmylua_code_analysis::LuaTypeDeclId::new(&type_name);
158+
type_index.get_type_decl(&type_id)?;
159+
160+
// Get all subclasses
161+
let sub_types = type_index.get_all_sub_types(&type_id);
162+
163+
// Build locations from subclasses
164+
let mut locations = Vec::new();
165+
for sub_type in sub_types {
166+
for location in sub_type.get_locations() {
167+
let file_id = location.file_id;
168+
if let Some(document) = db.get_vfs().get_document(&file_id) {
169+
if let Some(lsp_range) = document.to_lsp_range(location.range) {
170+
locations.push(GutterLocation {
171+
uri: document.get_uri().to_string(),
172+
line: lsp_range.start.line as i32,
173+
kind: GutterKind::Class,
174+
});
175+
}
176+
}
177+
}
178+
}
179+
180+
Some(GutterDetailResponse { locations })
181+
}

crates/emmylua_ls/src/handlers/inlay_hint/build_inlay_hint.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,15 @@ use rowan::NodeOrToken;
1717

1818
use rowan::TokenAtOffset;
1919

20+
use crate::context::ClientId;
2021
use crate::handlers::completion::get_index_alias_name;
2122
use crate::handlers::definition::compare_function_types;
2223
use crate::handlers::inlay_hint::build_function_hint::{build_closure_hint, build_label_parts};
2324

24-
pub fn build_inlay_hints(semantic_model: &SemanticModel) -> Option<Vec<InlayHint>> {
25+
pub fn build_inlay_hints(
26+
semantic_model: &SemanticModel,
27+
client_id: ClientId,
28+
) -> Option<Vec<InlayHint>> {
2529
let mut result = Vec::new();
2630
let root = semantic_model.get_root();
2731
for node in root.clone().descendants::<LuaAst>() {
@@ -39,6 +43,9 @@ pub fn build_inlay_hints(semantic_model: &SemanticModel) -> Option<Vec<InlayHint
3943
build_local_name_hint(semantic_model, &mut result, local_name);
4044
}
4145
LuaAst::LuaFuncStat(func_stat) => {
46+
if client_id.is_intellij() {
47+
continue;
48+
}
4249
build_func_stat_override_hint(semantic_model, &mut result, func_stat);
4350
}
4451
LuaAst::LuaIndexExpr(index_expr) => {
@@ -426,7 +433,7 @@ fn build_func_stat_override_hint(
426433
Some(())
427434
}
428435

429-
fn get_super_member_id(
436+
pub fn get_super_member_id(
430437
semantic_model: &SemanticModel,
431438
super_type: LuaType,
432439
member_key: &LuaMemberKey,
@@ -449,7 +456,7 @@ fn get_super_member_id(
449456
None
450457
}
451458

452-
fn get_override_lsp_location(
459+
pub fn get_override_lsp_location(
453460
semantic_model: &SemanticModel,
454461
file_id: FileId,
455462
syntax_id: LuaSyntaxId,

crates/emmylua_ls/src/handlers/inlay_hint/mod.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,40 @@
11
mod build_function_hint;
22
mod build_inlay_hint;
33

4+
use super::RegisterCapabilities;
5+
use crate::context::{ClientId, ServerContextSnapshot};
46
use build_inlay_hint::build_inlay_hints;
7+
pub use build_inlay_hint::{get_override_lsp_location, get_super_member_id};
58
use emmylua_code_analysis::{EmmyLuaAnalysis, FileId};
69
use lsp_types::{
710
ClientCapabilities, InlayHint, InlayHintOptions, InlayHintParams, InlayHintServerCapabilities,
811
OneOf, ServerCapabilities,
912
};
1013
use tokio_util::sync::CancellationToken;
1114

12-
use crate::context::ServerContextSnapshot;
13-
14-
use super::RegisterCapabilities;
15-
1615
pub async fn on_inlay_hint_handler(
1716
context: ServerContextSnapshot,
1817
params: InlayHintParams,
1918
_: CancellationToken,
2019
) -> Option<Vec<InlayHint>> {
2120
let uri = params.text_document.uri;
2221
let analysis = context.analysis().read().await;
23-
inlay_hint(&analysis, analysis.get_file_id(&uri)?)
22+
let client_id = context
23+
.workspace_manager()
24+
.read()
25+
.await
26+
.client_config
27+
.client_id;
28+
inlay_hint(&analysis, analysis.get_file_id(&uri)?, client_id)
2429
}
2530

26-
pub fn inlay_hint(analysis: &EmmyLuaAnalysis, file_id: FileId) -> Option<Vec<InlayHint>> {
31+
pub fn inlay_hint(
32+
analysis: &EmmyLuaAnalysis,
33+
file_id: FileId,
34+
client_id: ClientId,
35+
) -> Option<Vec<InlayHint>> {
2736
let semantic_model = analysis.compilation.get_semantic_model(file_id)?;
28-
build_inlay_hints(&semantic_model)
37+
build_inlay_hints(&semantic_model, client_id)
2938
}
3039

3140
#[allow(unused_variables)]

0 commit comments

Comments
 (0)