diff --git a/components/clarity-lsp/src/common/requests/api_ref.rs b/components/clarity-lsp/src/common/requests/api_ref.rs index f4ec7a395..2d6ce61f3 100644 --- a/components/clarity-lsp/src/common/requests/api_ref.rs +++ b/components/clarity-lsp/src/common/requests/api_ref.rs @@ -22,7 +22,7 @@ lazy_static! { api_references.insert( define_function.to_string(), (reference.version, Vec::from([ - &code(format!("{:} -> {:}", &reference.signature, &reference.output_type).as_str()), + &code(&reference.signature), separator, "**Description**", &reference.description, @@ -39,7 +39,7 @@ lazy_static! { api_references.insert( native_function.to_string(), (reference.version, Vec::from([ - &code(format!("{:} -> {:}", &reference.signature, &reference.output_type).as_str()), + &code(format!("{} -> {}", &reference.signature, &reference.output_type).as_str()), separator, "**Description**", &reference.description, diff --git a/components/clarity-lsp/src/common/requests/completion.rs b/components/clarity-lsp/src/common/requests/completion.rs index ca0dfe23a..c6661dd39 100644 --- a/components/clarity-lsp/src/common/requests/completion.rs +++ b/components/clarity-lsp/src/common/requests/completion.rs @@ -1,9 +1,15 @@ -use clarity_repl::clarity::{ - docs::{make_api_reference, make_define_reference, make_keyword_reference}, - functions::{define::DefineFunctions, NativeFunctions}, - variables::NativeVariables, - vm::types::BlockInfoProperty, - ClarityVersion, SymbolicExpression, +use std::{collections::HashMap, vec}; + +use clarity_repl::{ + analysis::ast_visitor::{traverse, ASTVisitor, TypedVar}, + clarity::{ + analysis::ContractAnalysis, + docs::{make_api_reference, make_define_reference, make_keyword_reference}, + functions::{define::DefineFunctions, NativeFunctions}, + variables::NativeVariables, + vm::types::{BlockInfoProperty, FunctionType, TypeSignature}, + ClarityName, ClarityVersion, SymbolicExpression, + }, }; use lazy_static::lazy_static; use lsp_types::{ @@ -11,6 +17,8 @@ use lsp_types::{ Position, }; +use super::helpers::is_position_within_span; + lazy_static! { static ref COMPLETION_ITEMS_CLARITY_1: Vec = build_default_native_keywords_list(ClarityVersion::Clarity1); @@ -43,66 +51,79 @@ lazy_static! { #[derive(Clone, Debug, Default)] pub struct ContractDefinedData { + position: Position, + consts: Vec<(String, String)>, + locals: Vec<(String, String)>, pub vars: Vec, pub maps: Vec, pub fts: Vec, pub nfts: Vec, - pub consts: Vec<(String, String)>, + pub functions_completion_items: Vec, } -impl ContractDefinedData { - pub fn new(expressions: &Vec) -> Self { - let mut defined_data = ContractDefinedData::default(); - for expression in expressions { - expression - .match_list() - .and_then(|list| list.split_first()) - .and_then(|(function_name, args)| { - Some(( - DefineFunctions::lookup_by_name(function_name.match_atom()?), - args.first()?.match_atom()?.to_string(), - args, - )) - }) - .and_then(|(define_function, name, args)| { - match define_function { - Some(DefineFunctions::PersistedVariable) => defined_data.vars.push(name), - Some(DefineFunctions::Map) => defined_data.maps.push(name), - Some(DefineFunctions::FungibleToken) => defined_data.fts.push(name), - Some(DefineFunctions::NonFungibleToken) => defined_data.nfts.push(name), - Some(DefineFunctions::Constant) => { - defined_data.consts.push((name, args.last()?.to_string())) - } - _ => (), - }; - Some(()) - }); - } +impl<'a> ContractDefinedData { + pub fn new(expressions: &Vec, position: &Position) -> Self { + let mut defined_data = ContractDefinedData { + position: position.clone(), + ..Default::default() + }; + traverse(&mut defined_data, &expressions); defined_data } + // this methods is in charge of: + // 1. set the function completion item with its arguments + // 2. set the local binding names if the position is within this function + fn set_function_completion_with_bindings( + &mut self, + expr: &SymbolicExpression, + name: &ClarityName, + parameters: &Vec>, + ) { + let mut completion_args: Vec = vec![]; + for (i, typed_var) in parameters.iter().enumerate() { + if let Ok(signature) = TypeSignature::parse_type_repr(typed_var.type_expr, &mut ()) { + completion_args.push(format!("${{{}:{}:{}}}", i + 1, typed_var.name, signature)); + + if is_position_within_span(&self.position, &expr.span, 0) { + self.locals + .push((typed_var.name.to_string(), signature.to_string())); + } + }; + } + + self.functions_completion_items.push(CompletionItem { + label: name.to_string(), + kind: Some(CompletionItemKind::MODULE), + insert_text: Some(format!("{} {}", name, completion_args.join(" "))), + insert_text_format: Some(InsertTextFormat::SNIPPET), + ..Default::default() + }); + } + pub fn populate_snippet_with_options(&self, name: &String, snippet: &String) -> Option { if VAR_FUNCTIONS.contains(name) && self.vars.len() > 0 { let choices = self.vars.join(","); - return Some(snippet.replace("${1:var}", &format!("${{1|{:}|}}", choices))); + return Some(snippet.replace("${1:var}", &format!("${{1|{}|}}", choices))); } if MAP_FUNCTIONS.contains(name) && self.maps.len() > 0 { let choices = self.maps.join(","); - return Some(snippet.replace("${1:map-name}", &format!("${{1|{:}|}}", choices))); + return Some(snippet.replace("${1:map-name}", &format!("${{1|{}|}}", choices))); } if FT_FUNCTIONS.contains(name) && self.fts.len() > 0 { let choices = self.fts.join(","); - return Some(snippet.replace("${1:token-name}", &format!("${{1|{:}|}}", choices))); + return Some(snippet.replace("${1:token-name}", &format!("${{1|{}|}}", choices))); } if NFT_FUNCTIONS.contains(name) && self.nfts.len() > 0 { let choices = self.nfts.join(","); - return Some(snippet.replace("${1:asset-name}", &format!("${{1|{:}|}}", choices))); + return Some(snippet.replace("${1:asset-name}", &format!("${{1|{}|}}", choices))); } None } - pub fn get_consts_completion_item(&self) -> Vec { - self.consts + pub fn get_contract_completion_items(&self) -> Vec { + [&self.consts[..], &self.locals[..]] + .concat() .iter() .map(|(name, definition)| { CompletionItem::new_simple(name.to_string(), definition.to_string()) @@ -111,10 +132,156 @@ impl ContractDefinedData { } } +impl<'a> ASTVisitor<'a> for ContractDefinedData { + fn visit_define_constant( + &mut self, + _expr: &'a SymbolicExpression, + name: &'a ClarityName, + value: &'a SymbolicExpression, + ) -> bool { + self.consts.push((name.to_string(), value.to_string())); + true + } + + fn visit_define_data_var( + &mut self, + _expr: &'a SymbolicExpression, + name: &'a ClarityName, + _data_type: &'a SymbolicExpression, + _initial: &'a SymbolicExpression, + ) -> bool { + self.vars.push(name.to_string()); + true + } + + fn visit_define_map( + &mut self, + _expr: &'a SymbolicExpression, + name: &'a ClarityName, + _key_type: &'a SymbolicExpression, + _value_type: &'a SymbolicExpression, + ) -> bool { + self.maps.push(name.to_string()); + true + } + + fn visit_define_ft( + &mut self, + _expr: &'a SymbolicExpression, + name: &'a ClarityName, + _supply: Option<&'a SymbolicExpression>, + ) -> bool { + self.fts.push(name.to_string()); + true + } + + fn visit_define_nft( + &mut self, + _expr: &'a SymbolicExpression, + name: &'a ClarityName, + _nft_type: &'a SymbolicExpression, + ) -> bool { + self.nfts.push(name.to_string()); + true + } + + fn visit_define_public( + &mut self, + expr: &'a SymbolicExpression, + name: &'a ClarityName, + parameters: Option>>, + _body: &'a SymbolicExpression, + ) -> bool { + if let Some(parameters) = parameters { + self.set_function_completion_with_bindings(expr, name, ¶meters); + } + true + } + + fn visit_define_read_only( + &mut self, + expr: &'a SymbolicExpression, + name: &'a ClarityName, + parameters: Option>>, + _body: &'a SymbolicExpression, + ) -> bool { + if let Some(parameters) = parameters { + self.set_function_completion_with_bindings(expr, name, ¶meters); + } + true + } + + fn visit_define_private( + &mut self, + expr: &'a SymbolicExpression, + name: &'a ClarityName, + parameters: Option>>, + _body: &'a SymbolicExpression, + ) -> bool { + if let Some(parameters) = parameters { + self.set_function_completion_with_bindings(expr, name, ¶meters); + } + true + } + + fn visit_let( + &mut self, + expr: &'a SymbolicExpression, + bindings: &HashMap<&'a ClarityName, &'a SymbolicExpression>, + _body: &'a [SymbolicExpression], + ) -> bool { + if is_position_within_span(&self.position, &expr.span, 0) { + for (name, value) in bindings { + self.locals.push((name.to_string(), value.to_string())); + } + } + true + } +} + +fn build_contract_calls_args(signature: &FunctionType) -> Vec { + let mut args = vec![]; + if let FunctionType::Fixed(function) = signature { + for (i, arg) in function.args.iter().enumerate() { + args.push(format!("${{{}:{}:{}}}", i + 1, arg.name, arg.signature)); + } + } + args +} + +pub fn get_contract_calls(analysis: &ContractAnalysis) -> Vec { + let mut inter_contract = vec![]; + for (name, signature) in analysis + .public_function_types + .iter() + .chain(analysis.read_only_function_types.iter()) + { + let label = format!( + "contract-call? .{} {}", + analysis.contract_identifier.name.to_string(), + name.to_string() + ); + let insert_text = format!( + "contract-call? .{} {} {}", + analysis.contract_identifier.name.to_string(), + name.to_string(), + build_contract_calls_args(signature).join(" ") + ); + inter_contract.push(CompletionItem { + label, + kind: Some(CompletionItemKind::EVENT), + insert_text: Some(insert_text), + insert_text_format: Some(InsertTextFormat::SNIPPET), + ..Default::default() + }); + } + inter_contract +} + pub fn build_completion_item_list( - contract_defined_data: &ContractDefinedData, clarity_version: &ClarityVersion, - user_defined_keywords: Vec, + active_contract_defined_data: &ContractDefinedData, + contract_calls: Vec, should_wrap: bool, include_native_placeholders: bool, ) -> Vec { @@ -123,8 +290,17 @@ pub fn build_completion_item_list( ClarityVersion::Clarity2 => COMPLETION_ITEMS_CLARITY_2.to_vec(), }; let mut completion_items = vec![]; - completion_items.append(&mut contract_defined_data.get_consts_completion_item()); - for mut item in [native_keywords, user_defined_keywords].concat().drain(..) { + completion_items.append(&mut active_contract_defined_data.get_contract_completion_items()); + for mut item in [ + native_keywords, + contract_calls, + active_contract_defined_data + .functions_completion_items + .clone(), + ] + .concat() + .drain(..) + { match item.kind { Some( CompletionItemKind::EVENT @@ -135,8 +311,8 @@ pub fn build_completion_item_list( let mut snippet = item.insert_text.take().unwrap(); let mut snippet_has_choices = false; if item.kind == Some(CompletionItemKind::FUNCTION) { - if let Some(populated_snippet) = - contract_defined_data.populate_snippet_with_options(&item.label, &snippet) + if let Some(populated_snippet) = active_contract_defined_data + .populate_snippet_with_options(&item.label, &snippet) { snippet_has_choices = true; snippet = populated_snippet; @@ -185,13 +361,11 @@ pub fn build_completion_item_list( } Some(CompletionItemKind::TYPE_PARAMETER) => { if should_wrap { - match item.label.as_str() { - "tuple" | "buff" | "string-ascii" | "string-utf8" | "optional" - | "response" | "principal" => { - item.insert_text = Some(format!("({} $0)", item.label)); - item.insert_text_format = Some(InsertTextFormat::SNIPPET); - } - _ => (), + if let "tuple" | "buff" | "string-ascii" | "string-utf8" | "optional" | "list" + | "response" = item.label.as_str() + { + item.insert_text = Some(format!("({} $0)", item.label)); + item.insert_text_format = Some(InsertTextFormat::SNIPPET); } } } @@ -365,10 +539,11 @@ pub fn build_default_native_keywords_list(version: ClarityVersion) -> Vec ContractDefinedData { + let contract_ast = build_ast_with_rules( + &QualifiedContractIdentifier::transient(), + source, + &mut (), + ClarityVersion::Clarity2, + StacksEpochId::Epoch21, + clarity_repl::clarity::ast::ASTRules::Typical, + ) + .unwrap(); + ContractDefinedData::new(&contract_ast.expressions, position) + } + + #[test] + fn get_function_binding() { + let data = get_defined_data( + "(define-private (print-arg (arg int)) )", + &Position { + line: 1, + character: 38, + }, + ); + assert_eq!(data.locals, vec![("arg".to_string(), "int".to_string())]); + let data = get_defined_data( + "(define-private (print-arg (arg int)) )", + &Position { + line: 1, + character: 40, + }, + ); + assert_eq!(data.locals, vec![]); + } + + #[test] + fn get_let_binding() { + let data = get_defined_data( + "(let ((n u0)) )", + &Position { + line: 1, + character: 15, + }, + ); + assert_eq!(data.locals, vec![("n".to_string(), "u0".to_string())]); + } +} + #[cfg(test)] mod populate_snippet_with_options_tests { use clarity_repl::clarity::ast::build_ast_with_rules; use clarity_repl::clarity::stacks_common::types::StacksEpochId; use clarity_repl::clarity::{vm::types::QualifiedContractIdentifier, ClarityVersion}; + use lsp_types::Position; use super::ContractDefinedData; @@ -430,7 +661,7 @@ mod populate_snippet_with_options_tests { clarity_repl::clarity::ast::ASTRules::Typical, ) .unwrap(); - ContractDefinedData::new(&contract_ast.expressions) + ContractDefinedData::new(&contract_ast.expressions, &Position::default()) } #[test] diff --git a/components/clarity-lsp/src/common/requests/definitions.rs b/components/clarity-lsp/src/common/requests/definitions.rs index d7905e391..70a02befb 100644 --- a/components/clarity-lsp/src/common/requests/definitions.rs +++ b/components/clarity-lsp/src/common/requests/definitions.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use super::helpers::span_to_range; use clarity_repl::analysis::ast_visitor::{traverse, ASTVisitor, TypedVar}; +use clarity_repl::clarity::functions::define::DefineFunctions; use clarity_repl::clarity::vm::types::{QualifiedContractIdentifier, StandardPrincipalData}; use clarity_repl::clarity::{ClarityName, SymbolicExpression}; use lsp_types::Range; @@ -40,14 +41,18 @@ impl<'a> Definitions { traverse(self, &expressions); } - fn set_function_paramaters_scope(&mut self, expr: &SymbolicExpression) -> Option<()> { + fn set_function_parameters_scope(&mut self, expr: &SymbolicExpression) -> Option<()> { let mut local_scope = HashMap::new(); let (_, binding_exprs) = expr.match_list()?.get(1)?.match_list()?.split_first()?; for binding in binding_exprs { - let (name, _) = binding.match_list()?.split_first()?; - local_scope.insert(name.match_atom()?.to_owned(), span_to_range(&binding.span)); + if let Some(name) = binding + .match_list() + .and_then(|l| l.split_first()) + .and_then(|(name, _)| name.match_atom()) + { + local_scope.insert(name.to_owned(), span_to_range(&binding.span)); + } } - self.local.insert(expr.id, local_scope); Some(()) } @@ -322,7 +327,7 @@ impl<'a> ASTVisitor<'a> for Definitions { let identifier = if identifier.issuer == StandardPrincipalData::transient() { match &self.deployer { Some(deployer) => QualifiedContractIdentifier::parse(&format!( - "{:}.{:}", + "{}.{}", deployer, identifier.name )) .expect("failed to set contract name"), @@ -349,7 +354,7 @@ impl<'a> ASTVisitor<'a> for Definitions { parameters: Option>>, body: &'a SymbolicExpression, ) -> bool { - self.set_function_paramaters_scope(expr); + self.set_function_parameters_scope(expr); self.traverse_expr(body) && self.visit_define_private(expr, name, parameters, body) } @@ -371,7 +376,7 @@ impl<'a> ASTVisitor<'a> for Definitions { parameters: Option>>, body: &'a SymbolicExpression, ) -> bool { - self.set_function_paramaters_scope(expr); + self.set_function_parameters_scope(expr); self.traverse_expr(body) && self.visit_define_read_only(expr, name, parameters, body) } @@ -393,7 +398,7 @@ impl<'a> ASTVisitor<'a> for Definitions { parameters: Option>>, body: &'a SymbolicExpression, ) -> bool { - self.set_function_paramaters_scope(expr); + self.set_function_parameters_scope(expr); self.traverse_expr(body) && self.visit_define_public(expr, name, parameters, body) } @@ -468,10 +473,16 @@ impl<'a> ASTVisitor<'a> for Definitions { ) -> bool { let local_scope = || -> Option> { let mut result = HashMap::new(); + let binding_exprs = expr.match_list()?.get(1)?.match_list()?; for binding in binding_exprs { - let (name, _) = binding.match_list()?.split_first()?; - result.insert(name.match_atom()?.to_owned(), span_to_range(&binding.span)); + if let Some(name) = binding + .match_list() + .and_then(|l| l.split_first()) + .and_then(|(name, _)| name.match_atom()) + { + result.insert(name.to_owned(), span_to_range(&binding.span)); + } } Some(result) }; @@ -543,6 +554,36 @@ pub fn get_definitions( definitions_visitor.tokens } +pub fn get_public_function_definitions( + expressions: &Vec, +) -> HashMap { + let mut definitions = HashMap::new(); + + for expression in expressions { + if let Some((function_name, args)) = expression + .match_list() + .and_then(|l| l.split_first()) + .and_then(|(function_name, args)| Some((function_name.match_atom()?, args))) + { + match DefineFunctions::lookup_by_name(function_name) { + Some(DefineFunctions::PublicFunction | DefineFunctions::ReadOnlyFunction) => { + if let Some(function_name) = args + .split_first() + .and_then(|(args_list, _)| args_list.match_list()?.split_first()) + .and_then(|(function_name, _)| function_name.match_atom()) + { + definitions + .insert(function_name.to_owned(), span_to_range(&expression.span)); + } + } + _ => (), + } + } + } + + definitions +} + #[cfg(test)] mod definitions_visitor_tests { use std::collections::HashMap; diff --git a/components/clarity-lsp/src/common/requests/helpers.rs b/components/clarity-lsp/src/common/requests/helpers.rs index 31fc16de9..9ade6bb3d 100644 --- a/components/clarity-lsp/src/common/requests/helpers.rs +++ b/components/clarity-lsp/src/common/requests/helpers.rs @@ -1,14 +1,6 @@ -use std::collections::HashMap; - -use clarity_repl::clarity::{ - functions::define::DefineFunctions, representations::Span, ClarityName, SymbolicExpression, -}; +use clarity_repl::clarity::{representations::Span, ClarityName, SymbolicExpression}; use lsp_types::{Position, Range}; -#[cfg(feature = "wasm")] -#[allow(unused_imports)] -use crate::utils::log; - pub fn span_to_range(span: &Span) -> Range { if span == &Span::zero() { return Range::default(); @@ -68,26 +60,3 @@ pub fn get_atom_start_at_position( } None } - -pub fn get_public_function_definitions( - expressions: &Vec, -) -> Option> { - let mut definitions = HashMap::new(); - - for expression in expressions { - let (define_function, args) = expression.match_list()?.split_first()?; - match DefineFunctions::lookup_by_name(define_function.match_atom()?)? { - DefineFunctions::PublicFunction | DefineFunctions::ReadOnlyFunction => { - let (args_list, _) = args.split_first()?; - let (function_name, _) = args_list.match_list()?.split_first()?; - definitions.insert( - function_name.match_atom()?.to_owned(), - span_to_range(&expression.span), - ); - } - _ => (), - } - } - - Some(definitions) -} diff --git a/components/clarity-lsp/src/common/requests/signature_help.rs b/components/clarity-lsp/src/common/requests/signature_help.rs index 279141d17..1aa7011bd 100644 --- a/components/clarity-lsp/src/common/requests/signature_help.rs +++ b/components/clarity-lsp/src/common/requests/signature_help.rs @@ -80,7 +80,7 @@ pub fn get_signatures( SignatureInformation { active_parameter, documentation: None, - label: format!("{:} -> {:}", &signature, &output_type), + label: format!("{} -> {}", &signature, &output_type), parameters: Some( parameters .iter() @@ -160,7 +160,7 @@ mod definitions_visitor_tests { continue; } - let src = format!("({:} )", &method); + let src = format!("({} )", &method); let signatures = get_source_signature( src.as_str(), &Position { diff --git a/components/clarity-lsp/src/common/state.rs b/components/clarity-lsp/src/common/state.rs index 56b509c83..d1ace2fe9 100644 --- a/components/clarity-lsp/src/common/state.rs +++ b/components/clarity-lsp/src/common/state.rs @@ -1,6 +1,4 @@ use crate::common::requests::completion::check_if_should_wrap; -use crate::types::CompletionMaps; -use crate::utils; use chainhook_types::StacksNetwork; use clarinet_deployments::{ generate_default_deployment, initiate_session_from_deployment, @@ -27,10 +25,14 @@ use std::collections::{BTreeMap, HashMap, HashSet}; use std::vec; use super::requests::capabilities::InitializationOptions; -use super::requests::completion::{build_completion_item_list, ContractDefinedData}; -use super::requests::definitions::{get_definitions, DefinitionLocation}; +use super::requests::completion::{ + build_completion_item_list, get_contract_calls, ContractDefinedData, +}; +use super::requests::definitions::{ + get_definitions, get_public_function_definitions, DefinitionLocation, +}; use super::requests::document_symbols::ASTSymbols; -use super::requests::helpers::{get_atom_start_at_position, get_public_function_definitions}; +use super::requests::helpers::get_atom_start_at_position; use super::requests::hover::get_expression_documentation; use super::requests::signature_help::get_signatures; @@ -125,7 +127,7 @@ impl ActiveContractData { #[derive(Debug, Clone, PartialEq)] pub struct ContractState { - intellisense: CompletionMaps, + contract_calls: Vec, errors: Vec, warnings: Vec, notes: Vec, @@ -165,14 +167,14 @@ impl ContractState { } } - let intellisense = match analysis { - Some(ref analysis) => utils::build_intellisense(&analysis), - None => CompletionMaps::default(), + let contract_calls = match analysis { + Some(ref analysis) => get_contract_calls(analysis), + None => vec![], }; ContractState { contract_id, - intellisense, + contract_calls, errors, warnings, notes, @@ -301,15 +303,17 @@ impl EditorState { None => return vec![], }; - let user_defined_keywords = self + let contract_calls = self .contracts_lookup .get(contract_location) .and_then(|d| self.protocols.get(&d.manifest_location)) - .and_then(|p| Some(p.get_completion_items_for_contract(contract_location))) + .and_then(|p| Some(p.get_contract_calls_for_contract(contract_location))) .unwrap_or_default(); - let contract_defined_data = - ContractDefinedData::new(active_contract.expressions.as_ref().unwrap_or(&vec![])); + let active_contract_defined_data = ContractDefinedData::new( + &active_contract.expressions.as_ref().unwrap_or(&vec![]), + position, + ); let should_wrap = match self.settings.completion_smart_parenthesis_wrap { true => check_if_should_wrap(&active_contract.source, position), @@ -317,9 +321,9 @@ impl EditorState { }; build_completion_item_list( - &contract_defined_data, &active_contract.clarity_version, - user_defined_keywords, + &active_contract_defined_data, + contract_calls, should_wrap, self.settings.completion_include_native_placeholders, ) @@ -380,7 +384,7 @@ impl EditorState { .get(definition_contract_location) .and_then(|c| c.expressions.as_ref()) { - let public_definitions = get_public_function_definitions(&expressions)?; + let public_definitions = get_public_function_definitions(&expressions); return Some(Location { range: *public_definitions.get(function_name)?, uri: Url::parse(&definition_contract_location.to_string()).ok()?, @@ -611,29 +615,17 @@ impl ProtocolState { } } - pub fn get_completion_items_for_contract( + pub fn get_contract_calls_for_contract( &self, contract_uri: &FileLocation, ) -> Vec { - let mut keywords = vec![]; - - let (mut contract_keywords, mut contract_calls) = { - let contract_keywords = match self.contracts.get(&contract_uri) { - Some(entry) => entry.intellisense.intra_contract.clone(), - _ => vec![], - }; - let mut contract_calls = vec![]; - for (url, contract_state) in self.contracts.iter() { - if !contract_uri.eq(url) { - contract_calls.append(&mut contract_state.intellisense.inter_contract.clone()); - } + let mut contract_calls = vec![]; + for (url, contract_state) in self.contracts.iter() { + if !contract_uri.eq(url) { + contract_calls.append(&mut contract_state.contract_calls.clone()); } - (contract_keywords, contract_calls) - }; - - keywords.append(&mut contract_keywords); - keywords.append(&mut contract_calls); - keywords + } + contract_calls } } @@ -691,11 +683,10 @@ pub async fn build_state( match execution_result.result { EvaluationResult::Contract(contract_result) => { if let Some(ast) = artifacts.asts.get(&contract_id) { - if let Some(public_definitions) = - get_public_function_definitions(&ast.expressions) - { - definitions.insert(contract_id.clone(), public_definitions); - } + definitions.insert( + contract_id.clone(), + get_public_function_definitions(&ast.expressions), + ); } analyses .insert(contract_id.clone(), Some(contract_result.contract.analysis)); diff --git a/components/clarity-lsp/src/lib.rs b/components/clarity-lsp/src/lib.rs index 51805a227..8cd987abb 100644 --- a/components/clarity-lsp/src/lib.rs +++ b/components/clarity-lsp/src/lib.rs @@ -1,5 +1,4 @@ mod common; -pub mod types; pub mod utils; #[cfg(feature = "wasm")] pub mod vscode_bridge; diff --git a/components/clarity-lsp/src/types/mod.rs b/components/clarity-lsp/src/types/mod.rs deleted file mode 100644 index 53c0799aa..000000000 --- a/components/clarity-lsp/src/types/mod.rs +++ /dev/null @@ -1,24 +0,0 @@ -use lsp_types::CompletionItem; -use serde::{Deserialize, Serialize}; - -#[allow(dead_code)] -#[derive(Debug, Clone, Deserialize, Serialize)] -enum Symbol { - PublicFunction, - ReadonlyFunction, - PrivateFunction, - ImportedTrait, - LocalVariable, - Constant, - DataMap, - DataVar, - FungibleToken, - NonFungibleToken, -} - -#[derive(Debug, Clone, Default, PartialEq, Deserialize, Serialize)] -pub struct CompletionMaps { - pub inter_contract: Vec, - pub intra_contract: Vec, - pub data_fields: Vec, -} diff --git a/components/clarity-lsp/src/utils/mod.rs b/components/clarity-lsp/src/utils/mod.rs index 597269050..5aa741eb8 100644 --- a/components/clarity-lsp/src/utils/mod.rs +++ b/components/clarity-lsp/src/utils/mod.rs @@ -1,14 +1,12 @@ -use super::types::*; use clarinet_files::FileLocation; -use clarity_repl::clarity::vm::analysis::ContractAnalysis; use clarity_repl::clarity::vm::diagnostic::{ Diagnostic as ClarityDiagnostic, Level as ClarityLevel, }; -use clarity_repl::clarity::vm::types::FunctionType; -use lsp_types::{CompletionItem, CompletionItemKind, Diagnostic as LspDiagnostic}; +use lsp_types::Diagnostic as LspDiagnostic; +use lsp_types::Url; use lsp_types::{DiagnosticSeverity, Position, Range}; -use lsp_types::{InsertTextFormat, Url}; +#[allow(unused_macros)] #[cfg(feature = "wasm")] macro_rules! log { ( $( $t:tt )* ) => { @@ -59,102 +57,6 @@ pub fn clarity_diagnostic_to_lsp_type(diagnostic: &ClarityDiagnostic) -> LspDiag } } -fn build_intellisense_args(signature: &FunctionType) -> Vec { - let mut args = vec![]; - match signature { - FunctionType::Fixed(function) => { - for (i, arg) in function.args.iter().enumerate() { - args.push(format!("${{{}:{}:{}}}", i + 1, arg.name, arg.signature)); - } - } - _ => {} - } - args -} - -pub fn build_intellisense(analysis: &ContractAnalysis) -> CompletionMaps { - let mut intra_contract = vec![]; - let mut inter_contract = vec![]; - - for (name, signature) in analysis.public_function_types.iter() { - let insert_text = format!("{} {}", name, build_intellisense_args(signature).join(" ")); - intra_contract.push(CompletionItem { - label: name.to_string(), - kind: Some(CompletionItemKind::MODULE), - insert_text: Some(insert_text), - insert_text_format: Some(InsertTextFormat::SNIPPET), - ..Default::default() - }); - - let label = format!( - "contract-call::{}::{}", - analysis.contract_identifier.name.to_string(), - name.to_string() - ); - let _insert = format!("{} {}", name, build_intellisense_args(signature).join(" ")); - let insert_text = format!( - "contract-call? .{} {} {}", - analysis.contract_identifier.name.to_string(), - name.to_string(), - build_intellisense_args(signature).join(" ") - ); - inter_contract.push(CompletionItem { - label, - kind: Some(CompletionItemKind::EVENT), - insert_text: Some(insert_text), - insert_text_format: Some(InsertTextFormat::SNIPPET), - ..Default::default() - }); - } - - for (name, signature) in analysis.read_only_function_types.iter() { - let insert_text = format!("{} {}", name, build_intellisense_args(signature).join(" ")); - intra_contract.push(CompletionItem { - label: name.to_string(), - kind: Some(CompletionItemKind::MODULE), - insert_text: Some(insert_text), - insert_text_format: Some(InsertTextFormat::SNIPPET), - ..Default::default() - }); - - let label = format!( - "contract-call::{}::{}", - analysis.contract_identifier.name.to_string(), - name.to_string() - ); - let insert_text = format!( - "contract-call? .{} {} {}", - analysis.contract_identifier.name.to_string(), - name.to_string(), - build_intellisense_args(signature).join(" ") - ); - inter_contract.push(CompletionItem { - label, - kind: Some(CompletionItemKind::EVENT), - insert_text: Some(insert_text), - insert_text_format: Some(InsertTextFormat::SNIPPET), - ..Default::default() - }); - } - - for (name, signature) in analysis.private_function_types.iter() { - let insert_text = format!("{} {}", name, build_intellisense_args(signature).join(" ")); - intra_contract.push(CompletionItem { - label: name.to_string(), - kind: Some(CompletionItemKind::MODULE), - insert_text: Some(insert_text), - insert_text_format: Some(InsertTextFormat::SNIPPET), - ..Default::default() - }); - } - - CompletionMaps { - inter_contract, - intra_contract, - data_fields: vec![], - } -} - pub fn get_manifest_location(text_document_uri: &Url) -> Option { let file_location = text_document_uri.to_string(); if !file_location.ends_with("Clarinet.toml") { diff --git a/components/clarity-lsp/src/vscode_bridge.rs b/components/clarity-lsp/src/vscode_bridge.rs index 8b0d23117..63d8af633 100644 --- a/components/clarity-lsp/src/vscode_bridge.rs +++ b/components/clarity-lsp/src/vscode_bridge.rs @@ -4,9 +4,7 @@ use crate::backend::{ LspRequestResponse, }; use crate::state::EditorState; -use crate::utils::{ - clarity_diagnostics_to_lsp_type, get_contract_location, get_manifest_location, log, -}; +use crate::utils::{clarity_diagnostics_to_lsp_type, get_contract_location, get_manifest_location}; use clarinet_files::{FileAccessor, WASMFileSystemAccessor}; use js_sys::{Function as JsFunction, Promise}; use lsp_types::notification::{ @@ -28,6 +26,9 @@ use std::sync::{Arc, RwLock}; use wasm_bindgen::prelude::*; use wasm_bindgen_futures::future_to_promise; +#[cfg(debug_assertions)] +use crate::utils::log; + #[wasm_bindgen] pub struct LspVscodeBridge { client_diagnostic_tx: JsFunction,