Skip to content

Reuse doc completion, hover, and definition for @see #698

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -389,11 +389,16 @@ pub fn analyze_see(analyzer: &mut DocAnalyzer, tag: LuaDocTagSee) -> Option<()>
let owner = get_owner_id_or_report(analyzer, &tag)?;
let content = tag.get_see_content()?;
let text = content.get_text();

analyzer
.db
.get_property_index_mut()
.add_see(analyzer.file_id, owner, text.to_string());
let descriptions = tag
.get_description()
.map(|description| description.get_description_text());

analyzer.db.get_property_index_mut().add_see(
analyzer.file_id,
owner,
text.to_string(),
descriptions,
);

Some(())
}
Expand Down
8 changes: 7 additions & 1 deletion crates/emmylua_code_analysis/src/db_index/property/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,19 @@ impl LuaPropertyIndex {
&mut self,
file_id: FileId,
owner_id: LuaSemanticDeclId,
see_content: String,
mut see_content: String,
see_description: Option<String>,
) -> Option<()> {
let property = self.get_or_create_property(owner_id.clone())?;
let tag_content = property
.tag_content
.get_or_insert_with(|| Box::new(LuaTagContent::new()));

if let Some(see_description) = see_description {
see_content += " ";
see_content += &see_description;
}

tag_content.add_tag("see".into(), see_content);

self.in_filed_owner
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use crate::handlers::completion::providers::member_provider::add_completions_for
use crate::handlers::completion::providers::module_path_provider::add_modules;
use crate::util::{find_comment_scope, find_ref_at, resolve_ref};
use emmylua_code_analysis::{LuaType, WorkspaceId};
use emmylua_parser::{LuaAstNode, LuaDocDescription};
use emmylua_parser_desc::LuaDescRefPathItem;
use emmylua_parser::{LuaAstNode, LuaDocDescription, LuaTokenKind};
use emmylua_parser_desc::{LuaDescRefPathItem, parse_ref_target};
use rowan::TextRange;
use std::collections::HashSet;

Expand All @@ -18,28 +18,41 @@ pub fn add_completions(builder: &mut CompletionBuilder) -> Option<()> {

let semantic_model = &builder.semantic_model;
let document = semantic_model.get_document();
let description = LuaDocDescription::cast(builder.trigger_token.parent()?)?;

// Quickly scan the line before actually parsing comment.
let line = document.get_line(builder.position_offset)?;
let line_range = document.get_line_range(line)?;
let line_text =
&document.get_text()[line_range.intersect(TextRange::up_to(builder.position_offset))?];
let path = if let Some(description) = builder
.trigger_token
.parent()
.and_then(LuaDocDescription::cast)
{
// Quickly scan the line before actually parsing comment.
let line = document.get_line(builder.position_offset)?;
let line_range = document.get_line_range(line)?;
let line_text = &document.get_text()
[line_range.intersect(TextRange::up_to(builder.position_offset))?];

if !line_text.contains('`') {
return None;
}

if !line_text.contains('`') {
find_ref_at(
semantic_model
.get_module()
.map(|m| m.workspace_id)
.unwrap_or(WorkspaceId::MAIN),
semantic_model.get_emmyrc(),
document.get_text(),
description,
builder.position_offset,
)?
} else if builder.trigger_token.kind() == LuaTokenKind::TkDocSeeContent.into() {
parse_ref_target(
document.get_text(),
builder.trigger_token.text_range(),
builder.position_offset,
)?
} else {
return None;
}

let path = find_ref_at(
semantic_model
.get_module()
.map(|m| m.workspace_id)
.unwrap_or(WorkspaceId::MAIN),
semantic_model.get_emmyrc(),
document.get_text(),
description.clone(),
builder.position_offset,
)?;
};

if path.is_empty() {
add_global_completions(builder);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ fn check_can_add_type_completion(builder: &CompletionBuilder) -> Option<()> {
LuaTokenKind::TkWhitespace => {
let left_token = builder.trigger_token.prev_token()?;
match left_token.kind().into() {
LuaTokenKind::TkTagReturn | LuaTokenKind::TkTagType | LuaTokenKind::TkTagSee => {
LuaTokenKind::TkTagReturn | LuaTokenKind::TkTagType => {
return Some(());
}
LuaTokenKind::TkName => {
Expand Down
44 changes: 20 additions & 24 deletions crates/emmylua_ls/src/handlers/definition/goto_doc_see.rs
Original file line number Diff line number Diff line change
@@ -1,45 +1,41 @@
use emmylua_code_analysis::{LuaMemberKey, LuaSemanticDeclId, LuaType, SemanticModel};
use crate::handlers::definition::goto_path::goto_path;
use emmylua_code_analysis::{
LuaCompilation, LuaMemberKey, LuaSemanticDeclId, LuaType, SemanticModel,
};
use emmylua_parser::{LuaAstToken, LuaGeneralToken};
use emmylua_parser_desc::parse_ref_target;
use lsp_types::GotoDefinitionResponse;
use rowan::TextSize;

pub fn goto_doc_see(
semantic_model: &SemanticModel,
compilation: &LuaCompilation,
content_token: LuaGeneralToken,
position_offset: TextSize,
) -> Option<GotoDefinitionResponse> {
let text = content_token.get_text();
let name_parts = text.split('#').collect::<Vec<_>>();

match name_parts.len() {
1 => {
let name = &name_parts[0];
return goto_type(semantic_model, &name);
}
2 => {
0 => {}
// Legacy handler for format like `@see type#member`
2 if !name_parts[1].is_empty() && !name_parts[1].starts_with([' ', '\t']) => {
let type_name = &name_parts[0];
let member_name = &name_parts[1];
return goto_type_member(semantic_model, &type_name, &member_name);
}
_ => {}
}
_ => {
let path = parse_ref_target(
semantic_model.get_document().get_text(),
content_token.get_range(),
position_offset,
)?;

None
}

fn goto_type(semantic_model: &SemanticModel, type_name: &str) -> Option<GotoDefinitionResponse> {
let file_id = semantic_model.get_file_id();
let type_decl = semantic_model
.get_db()
.get_type_index()
.find_type_decl(file_id, type_name)?;
let locations = type_decl.get_locations();
let mut result = Vec::new();
for location in locations {
let document = semantic_model.get_document_by_file_id(location.file_id)?;
let lsp_location = document.to_lsp_location(location.range)?;
result.push(lsp_location);
return goto_path(semantic_model, compilation, &path, content_token.syntax());
}
}

Some(GotoDefinitionResponse::Array(result))
None
}

fn goto_type_member(
Expand Down
7 changes: 6 additions & 1 deletion crates/emmylua_ls/src/handlers/definition/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,12 @@ pub fn definition(
} else if token.kind() == LuaTokenKind::TkDocSeeContent.into() {
let general_token = LuaGeneralToken::cast(token.clone())?;
if let Some(_) = general_token.get_parent::<LuaDocTagSee>() {
return goto_doc_see(&semantic_model, general_token);
return goto_doc_see(
&semantic_model,
&analysis.compilation,
general_token,
position_offset,
);
}
} else if token.kind() == LuaTokenKind::TkDocDetail.into() {
let parent = token.parent()?;
Expand Down
5 changes: 4 additions & 1 deletion crates/emmylua_ls/src/handlers/hover/hover_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub struct HoverBuilder<'a> {
pub annotation_description: Vec<MarkedString>,
/// Type expansion, often used for alias types
pub type_expansion: Option<Vec<String>>,
/// see
/// For `@see` and unknown tags tags
tag_content: Option<Vec<(String, String)>>,

pub is_completion: bool,
Expand Down Expand Up @@ -239,6 +239,9 @@ impl<'a> HoverBuilder<'a> {
}

if let Some(tag_content) = &self.tag_content {
if !tag_content.is_empty() {
content.push_str("\n---\n");
}
for (tag_name, description) in tag_content {
content.push_str(&format!("\n@*{}* {}\n", tag_name, description));
}
Expand Down
31 changes: 25 additions & 6 deletions crates/emmylua_ls/src/handlers/hover/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ mod hover_humanize;
mod keyword_hover;
mod std_hover;

use super::RegisterCapabilities;
use crate::context::ServerContextSnapshot;
use crate::util::{find_ref_at, resolve_ref_single};
pub use build_hover::build_hover_content_for_completion;
use build_hover::build_semantic_info_hover;
use emmylua_code_analysis::{EmmyLuaAnalysis, FileId, WorkspaceId};
use emmylua_parser::{LuaAstNode, LuaDocDescription, LuaKind, LuaTokenKind};
use emmylua_parser::{LuaAstNode, LuaDocDescription, LuaTokenKind};
use emmylua_parser_desc::parse_ref_target;
pub use find_origin::{find_all_same_named_members, find_member_origin_owner};
pub use hover_builder::HoverBuilder;
pub use hover_humanize::infer_prefix_global_name;
Expand All @@ -22,10 +26,6 @@ use rowan::TokenAtOffset;
pub use std_hover::{hover_std_description, is_std};
use tokio_util::sync::CancellationToken;

use super::RegisterCapabilities;
use crate::context::ServerContextSnapshot;
use crate::util::{find_ref_at, resolve_ref_single};

pub async fn on_hover(
context: ServerContextSnapshot,
params: HoverParams,
Expand Down Expand Up @@ -82,7 +82,7 @@ pub fn hover(analysis: &EmmyLuaAnalysis, file_id: FileId, position: Position) ->
range: document.to_lsp_range(keywords.text_range()),
});
}
detail if detail.kind() == LuaKind::Token(LuaTokenKind::TkDocDetail) => {
detail if detail.kind() == LuaTokenKind::TkDocDetail.into() => {
let parent = detail.parent()?;
let description = LuaDocDescription::cast(parent)?;
let document = semantic_model.get_document();
Expand Down Expand Up @@ -111,6 +111,25 @@ pub fn hover(analysis: &EmmyLuaAnalysis, file_id: FileId, position: Position) ->
path.last()?.1,
)
}
doc_see if doc_see.kind() == LuaTokenKind::TkDocSeeContent.into() => {
let document = semantic_model.get_document();

let path =
parse_ref_target(document.get_text(), doc_see.text_range(), position_offset)?;

let db = analysis.compilation.get_db();
let semantic_info = resolve_ref_single(db, file_id, &path, &doc_see)?;

build_semantic_info_hover(
&analysis.compilation,
&semantic_model,
db,
&document,
doc_see,
semantic_info,
path.last()?.1,
)
}
_ => {
let semantic_info = semantic_model.get_semantic_info(token.clone().into())?;
let db = semantic_model.get_db();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,13 +216,20 @@ fn build_tokens_semantic_token(
SemanticTokenModifier::DOCUMENTATION,
);
}
LuaTokenKind::TKDocPath | LuaTokenKind::TkDocSeeContent => {
LuaTokenKind::TKDocPath => {
builder.push_with_modifier(
token,
SemanticTokenType::STRING,
SemanticTokenModifier::DOCUMENTATION,
);
}
LuaTokenKind::TkDocSeeContent => {
builder.push_with_modifier(
token,
SemanticTokenType::VARIABLE,
SemanticTokenModifier::DOCUMENTATION,
);
}
LuaTokenKind::TkDocRegion | LuaTokenKind::TkDocEndRegion => {
builder.push(token, SemanticTokenType::COMMENT);
}
Expand Down
33 changes: 33 additions & 0 deletions crates/emmylua_ls/src/handlers/test/completion_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2082,4 +2082,37 @@ mod tests {
));
Ok(())
}

#[gtest]
fn test_see_completion() -> Result<()> {
let mut ws = ProviderVirtualWorkspace::new();
ws.def(
r#"
---@class Meep
"#,
);
check!(ws.check_completion(
r#"
--- @see M<??>
"#,
vec![
VirtualCompletionItem {
label: "Meep".to_string(),
kind: CompletionItemKind::CLASS,
..Default::default()
},
VirtualCompletionItem {
label: "virtual_0".to_string(),
kind: CompletionItemKind::FILE,
..Default::default()
},
VirtualCompletionItem {
label: "virtual_1".to_string(),
kind: CompletionItemKind::FILE,
..Default::default()
},
],
));
Ok(())
}
}
Loading
Loading