Skip to content

Commit

Permalink
Cherry pick ls updates (#6205)
Browse files Browse the repository at this point in the history
  • Loading branch information
orizi committed Aug 13, 2024
2 parents 925afea + aec1559 commit 964a02d
Show file tree
Hide file tree
Showing 25 changed files with 510 additions and 68 deletions.
10 changes: 5 additions & 5 deletions crates/cairo-lang-doc/src/db.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use cairo_lang_defs::db::DefsGroup;
use cairo_lang_defs::ids::{LanguageElementId, LookupItemId};
use cairo_lang_parser::utils::SimpleParserDatabase;
use cairo_lang_syntax::node::db::SyntaxGroup;
use cairo_lang_syntax::node::kind::SyntaxKind;
use cairo_lang_utils::Upcast;
use itertools::Itertools;

use crate::documentable_item::DocumentableItemId;
use crate::markdown::cleanup_doc_markdown;

#[salsa::query_group(DocDatabase)]
Expand All @@ -15,14 +15,14 @@ pub trait DocGroup: Upcast<dyn DefsGroup> + Upcast<dyn SyntaxGroup> + SyntaxGrou
// be the best to convert all /// comments to #[doc] attrs before processing items by plugins,
// so that plugins would get a nice and clean syntax of documentation to manipulate further.
/// Gets the documentation above an item definition.
fn get_item_documentation(&self, item_id: LookupItemId) -> Option<String>;
fn get_item_documentation(&self, item_id: DocumentableItemId) -> Option<String>;

// TODO(mkaput): Add tests.
/// Gets the signature of an item (i.e., item without its body).
fn get_item_signature(&self, item_id: LookupItemId) -> String;
fn get_item_signature(&self, item_id: DocumentableItemId) -> String;
}

fn get_item_documentation(db: &dyn DocGroup, item_id: LookupItemId) -> Option<String> {
fn get_item_documentation(db: &dyn DocGroup, item_id: DocumentableItemId) -> Option<String> {
// Get the text of the item (trivia + definition)
let doc = item_id.stable_location(db.upcast()).syntax_node(db.upcast()).get_text(db.upcast());

Expand Down Expand Up @@ -57,7 +57,7 @@ fn get_item_documentation(db: &dyn DocGroup, item_id: LookupItemId) -> Option<St
(!doc.trim().is_empty()).then_some(doc)
}

fn get_item_signature(db: &dyn DocGroup, item_id: LookupItemId) -> String {
fn get_item_signature(db: &dyn DocGroup, item_id: DocumentableItemId) -> String {
let syntax_node = item_id.stable_location(db.upcast()).syntax_node(db.upcast());
let definition = match syntax_node.green_node(db.upcast()).kind {
SyntaxKind::ItemConstant
Expand Down
38 changes: 38 additions & 0 deletions crates/cairo-lang-doc/src/documentable_item.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use cairo_lang_defs::db::DefsGroup;
use cairo_lang_defs::diagnostic_utils::StableLocation;
use cairo_lang_defs::ids::{LanguageElementId, LookupItemId, MemberId, VariantId};

/// Item which documentation can be fetched from source code.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum DocumentableItemId {
LookupItem(LookupItemId),
Member(MemberId),
Variant(VariantId),
}

impl DocumentableItemId {
pub fn stable_location(&self, db: &dyn DefsGroup) -> StableLocation {
match self {
DocumentableItemId::LookupItem(lookup_item_id) => lookup_item_id.stable_location(db),
DocumentableItemId::Member(member_id) => member_id.stable_location(db),
DocumentableItemId::Variant(variant_id) => variant_id.stable_location(db),
}
}
}

impl From<LookupItemId> for DocumentableItemId {
fn from(value: LookupItemId) -> Self {
DocumentableItemId::LookupItem(value)
}
}

impl From<MemberId> for DocumentableItemId {
fn from(value: MemberId) -> Self {
DocumentableItemId::Member(value)
}
}
impl From<VariantId> for DocumentableItemId {
fn from(value: VariantId) -> Self {
DocumentableItemId::Variant(value)
}
}
1 change: 1 addition & 0 deletions crates/cairo-lang-doc/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod db;
pub mod documentable_item;
mod markdown;
7 changes: 6 additions & 1 deletion crates/cairo-lang-filesystem/src/span.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::iter::Sum;
use std::ops::{Add, Sub};
use std::ops::{Add, Range, Sub};

use crate::db::FilesGroup;
use crate::ids::FileId;
Expand Down Expand Up @@ -94,6 +94,11 @@ impl TextSpan {
Self { start: self.start, end: self.start }
}

/// Returns self.start..self.end as [`Range<usize>`]
pub fn to_str_range(&self) -> Range<usize> {
self.start.0.0 as usize..self.end.0.0 as usize
}

/// Convert this span to a [`TextPositionSpan`] in the file.
pub fn position_in_file(self, db: &dyn FilesGroup, file: FileId) -> Option<TextPositionSpan> {
let start = self.start.position_in_file(db, file)?;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ fn missing_traits_actions(
module_start_offset.position_in_file(db.upcast(), file_id).unwrap().to_lsp();
let relevant_methods = find_methods_for_type(db, resolver, ty, stable_ptr);
let current_module = db.find_module_containing_node(node)?;
let module_visible_traits = db.visible_traits_from_module(current_module);
let module_visible_traits = db.visible_traits_from_module(current_module)?;
let mut code_actions = vec![];
for method in relevant_methods {
let method_name = method.name(db.upcast());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ pub fn completion_for_method(

// If the trait is not in scope, add a use statement.
if !module_has_trait(db, module_id, trait_id)? {
if let Some(trait_path) = db.visible_traits_from_module(module_id).get(&trait_id) {
if let Some(trait_path) = db.visible_traits_from_module(module_id)?.get(&trait_id) {
additional_text_edits.push(TextEdit {
range: Range::new(position, position),
new_text: format!("use {};\n", trait_path),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use cairo_lang_defs::db::DefsGroup;
use cairo_lang_doc::db::DocGroup;
use cairo_lang_filesystem::ids::FileId;
use cairo_lang_syntax::node::ast::TerminalIdentifier;
use cairo_lang_syntax::node::TypedSyntaxNode;
Expand All @@ -7,7 +8,7 @@ use tower_lsp::lsp_types::Hover;

use crate::ide::hover::markdown_contents;
use crate::lang::db::AnalysisDatabase;
use crate::lang::inspect::defs::SymbolDef;
use crate::lang::inspect::defs::{MemberDef, SymbolDef};
use crate::lang::lsp::ToLsp;
use crate::markdown::{fenced_code_block, RULE};

Expand Down Expand Up @@ -41,6 +42,20 @@ pub fn definition(
}
md
}
SymbolDef::Member(MemberDef { member, structure }) => {
let mut md = String::new();

// Signature is the signature of the struct, so it makes sense that the definition
// path is too.
md += &fenced_code_block(&structure.definition_path(db));
md += &fenced_code_block(&structure.signature(db));

if let Some(doc) = db.get_item_documentation((*member).into()) {
md += RULE;
md += &doc;
}
md
}
};

Some(Hover {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use cairo_lang_filesystem::span::TextOffset;
use cairo_lang_parser::db::ParserGroup;
use cairo_lang_syntax as syntax;
use cairo_lang_syntax::node::ast::{self};
use cairo_lang_syntax::node::kind::SyntaxKind;
use cairo_lang_syntax::node::{SyntaxNode, TypedSyntaxNode};
use cairo_lang_syntax::node::{ast, SyntaxNode, TypedSyntaxNode};
use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;
use cairo_lang_utils::Upcast;
use tower_lsp::lsp_types::*;
Expand Down Expand Up @@ -69,16 +68,23 @@ impl SemanticTokensTraverser {
let maybe_semantic_kind = self
.offset_to_kind_lookahead
.remove(&node.offset())
.or_else(|| SemanticTokenKind::from_syntax_node(db, node));
.or_else(|| SemanticTokenKind::from_syntax_node(db, node.clone()));

if let Some(semantic_kind) = maybe_semantic_kind {
let EncodedToken { delta_line, delta_start } = self.encoder.encode(width);
data.push(SemanticToken {
delta_line,
delta_start,
length: width,
token_type: semantic_kind.as_u32(),
token_modifiers_bitset: 0,
});
let Some(text) = node.text(db) else { unreachable!() };

if text.contains('\n') {
// Split multiline token into multiple single line tokens.
for line in text.split_inclusive('\n') {
self.push_semantic_token(line.len() as u32, &semantic_kind, data);

if line.ends_with('\n') {
self.encoder.next_line();
}
}
} else {
self.push_semantic_token(width, &semantic_kind, data);
}
} else {
self.encoder.skip(width);
}
Expand Down Expand Up @@ -128,6 +134,23 @@ impl SemanticTokensTraverser {
}
}

fn push_semantic_token(
&mut self,
width: u32,
semantic_kind: &SemanticTokenKind,
data: &mut Vec<SemanticToken>,
) {
let EncodedToken { delta_line, delta_start } = self.encoder.encode(width);

data.push(SemanticToken {
delta_line,
delta_start,
length: width,
token_type: semantic_kind.as_u32(),
token_modifiers_bitset: 0,
});
}

fn mark_future_token(&mut self, offset: TextOffset, semantic_kind: SemanticTokenKind) {
self.offset_to_kind_lookahead.insert(offset, semantic_kind);
}
Expand Down
15 changes: 15 additions & 0 deletions crates/cairo-lang-language-server/src/lang/db/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,21 @@ pub trait LsSyntaxGroup: Upcast<dyn ParserGroup> {
find(TextPosition { col, ..position })
})
}

/// Finds first ancestor of a given kind.
fn first_ancestor_of_kind(&self, mut node: SyntaxNode, kind: SyntaxKind) -> Option<SyntaxNode> {
let db = self.upcast();
let syntax_db = db.upcast();

while let Some(parent) = node.parent() {
if parent.kind(syntax_db) == kind {
return Some(parent);
} else {
node = parent;
}
}
None
}
}

impl<T> LsSyntaxGroup for T where T: Upcast<dyn ParserGroup> + ?Sized {}
18 changes: 15 additions & 3 deletions crates/cairo-lang-language-server/src/lang/inspect/defs.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::iter;

use cairo_lang_defs::ids::{
LanguageElementId, LookupItemId, ModuleItemId, TopLevelLanguageElementId, TraitItemId,
LanguageElementId, LookupItemId, MemberId, ModuleItemId, TopLevelLanguageElementId, TraitItemId,
};
use cairo_lang_doc::db::DocGroup;
use cairo_lang_semantic::db::SemanticGroup;
Expand All @@ -20,6 +20,7 @@ use smol_str::SmolStr;
use tracing::error;

use crate::lang::db::{AnalysisDatabase, LsSemanticGroup};
use crate::lang::inspect::defs::SymbolDef::Member;
use crate::{find_definition, ResolvedItem};

/// Keeps information about the symbol that is being searched for/inspected.
Expand All @@ -30,6 +31,13 @@ pub enum SymbolDef {
Item(ItemDef),
Variable(VariableDef),
ExprInlineMacro(String),
Member(MemberDef),
}

/// Information about a struct member.
pub struct MemberDef {
pub member: MemberId,
pub structure: ItemDef,
}

impl SymbolDef {
Expand Down Expand Up @@ -81,6 +89,10 @@ impl SymbolDef {
ResolvedItem::Generic(ResolvedGenericItem::Variable(_)) => {
VariableDef::new(db, definition_node).map(Self::Variable)
}
ResolvedItem::Member(member_id) => Some(Member(MemberDef {
member: member_id,
structure: ItemDef::new(db, &definition_node)?,
})),
}
}
}
Expand Down Expand Up @@ -129,12 +141,12 @@ impl ItemDef {
pub fn signature(&self, db: &AnalysisDatabase) -> String {
let contexts = self.context_items.iter().copied().rev();
let this = iter::once(self.lookup_item_id);
contexts.chain(this).map(|item| db.get_item_signature(item)).join("\n")
contexts.chain(this).map(|item| db.get_item_signature(item.into())).join("\n")
}

/// Gets item documentation in a final form usable for display.
pub fn documentation(&self, db: &AnalysisDatabase) -> Option<String> {
db.get_item_documentation(self.lookup_item_id)
db.get_item_documentation(self.lookup_item_id.into())
}

/// Gets the full path (including crate name and defining trait/impl if applicable)
Expand Down
Loading

0 comments on commit 964a02d

Please sign in to comment.