Skip to content

Commit 665dce8

Browse files
committed
Move helper functions from backend.rs to utils.rs
1 parent c22c5ce commit 665dce8

3 files changed

Lines changed: 262 additions & 303 deletions

File tree

lsp/src/backend.rs

Lines changed: 9 additions & 301 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,13 @@ use tower_lsp_server::lsp_types::{
1414
DidSaveTextDocumentParams, DocumentSymbol, DocumentSymbolParams, DocumentSymbolResponse,
1515
ExecuteCommandParams, GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverParams,
1616
HoverProviderCapability, InitializeParams, InitializeResult, InitializedParams, Location,
17-
MarkupContent, MarkupKind, MessageType, OneOf, ParameterInformation, ParameterLabel, Range,
18-
ReferenceParams, SaveOptions, SemanticToken, SemanticTokenModifier, SemanticTokenType,
19-
SemanticTokens, SemanticTokensFullOptions, SemanticTokensLegend, SemanticTokensOptions,
20-
SemanticTokensParams, SemanticTokensResult, SemanticTokensServerCapabilities,
21-
ServerCapabilities, SignatureHelp, SignatureHelpOptions, SignatureHelpParams,
22-
SignatureInformation, SymbolKind, TextDocumentSyncCapability, TextDocumentSyncKind,
23-
TextDocumentSyncOptions, TextDocumentSyncSaveOptions, Uri, WorkDoneProgressOptions,
24-
WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities,
17+
MarkupContent, MarkupKind, MessageType, OneOf, Range, ReferenceParams, SaveOptions,
18+
SemanticToken, SemanticTokenModifier, SemanticTokenType, SemanticTokens,
19+
SemanticTokensFullOptions, SemanticTokensLegend, SemanticTokensOptions, SemanticTokensParams,
20+
SemanticTokensResult, SemanticTokensServerCapabilities, ServerCapabilities, SignatureHelp,
21+
SignatureHelpOptions, SignatureHelpParams, SymbolKind, TextDocumentSyncCapability,
22+
TextDocumentSyncKind, TextDocumentSyncOptions, TextDocumentSyncSaveOptions, Uri,
23+
WorkDoneProgressOptions, WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities,
2524
};
2625
use tower_lsp_server::{Client, LanguageServer};
2726

@@ -37,7 +36,8 @@ use crate::completion::{self, CompletionProvider};
3736
use crate::error::LspError;
3837
use crate::function::Functions;
3938
use crate::utils::{
40-
find_all_references, find_function_name_range, find_related_call, get_call_span,
39+
create_signature_info, find_all_references, find_builtin_signature, find_function_call_context,
40+
find_function_name_range, find_key_position, find_related_call, get_call_span,
4141
get_comments_from_lines, position_to_span, span_contains, span_to_positions,
4242
};
4343

@@ -854,224 +854,6 @@ fn validate_witness_file(text: &str) -> Vec<Diagnostic> {
854854
diagnostics
855855
}
856856

857-
/// Find the position of a key in the JSON text
858-
fn find_key_position(text: &str, key: &str) -> Option<tower_lsp_server::lsp_types::Position> {
859-
let search = format!("\"{}\"", key);
860-
for (line_num, line) in text.lines().enumerate() {
861-
if let Some(col) = line.find(&search) {
862-
return Some(tower_lsp_server::lsp_types::Position::new(
863-
line_num as u32,
864-
col as u32,
865-
));
866-
}
867-
}
868-
None
869-
}
870-
871-
/// Find the position of a value field within a witness object
872-
fn find_value_position(
873-
text: &str,
874-
witness_name: &str,
875-
field_name: &str,
876-
) -> Option<tower_lsp_server::lsp_types::Position> {
877-
let witness_search = format!("\"{}\"", witness_name);
878-
let field_search = format!("\"{}\"", field_name);
879-
let mut in_witness = false;
880-
881-
for (line_num, line) in text.lines().enumerate() {
882-
if line.contains(&witness_search) {
883-
in_witness = true;
884-
}
885-
if in_witness {
886-
if let Some(col) = line.find(&field_search) {
887-
return Some(tower_lsp_server::lsp_types::Position::new(
888-
line_num as u32,
889-
col as u32,
890-
));
891-
}
892-
// Check if we've exited the witness object (simple heuristic)
893-
if line.trim() == "}," || line.trim() == "}" {
894-
if !line.contains(&witness_search) {
895-
in_witness = false;
896-
}
897-
}
898-
}
899-
}
900-
None
901-
}
902-
903-
/// Find function call context from the current line.
904-
/// Returns (function_name, active_parameter_index) if inside a function call.
905-
fn find_function_call_context(line: &str) -> Option<(String, u32)> {
906-
let mut paren_depth = 0;
907-
let mut bracket_depth = 0;
908-
let mut angle_depth = 0;
909-
let mut last_open_paren = None;
910-
let mut comma_count = 0;
911-
912-
// Scan from the end to find the innermost unclosed function call
913-
for (i, ch) in line.chars().rev().enumerate() {
914-
let pos = line.len() - 1 - i;
915-
match ch {
916-
')' => paren_depth += 1,
917-
'(' => {
918-
if paren_depth > 0 {
919-
paren_depth -= 1;
920-
} else {
921-
// Found unclosed '(' - this is our function call
922-
last_open_paren = Some(pos);
923-
break;
924-
}
925-
}
926-
']' => bracket_depth += 1,
927-
'[' => {
928-
if bracket_depth > 0 {
929-
bracket_depth -= 1;
930-
}
931-
}
932-
'>' => angle_depth += 1,
933-
'<' => {
934-
if angle_depth > 0 {
935-
angle_depth -= 1;
936-
}
937-
}
938-
',' if paren_depth == 0 && bracket_depth == 0 && angle_depth == 0 => {
939-
comma_count += 1;
940-
}
941-
_ => {}
942-
}
943-
}
944-
945-
let open_paren_pos = last_open_paren?;
946-
947-
// Extract function name before the '('
948-
let before_paren = &line[..open_paren_pos];
949-
let func_name = extract_function_name(before_paren)?;
950-
951-
Some((func_name, comma_count))
952-
}
953-
954-
/// Extract function name from text before an opening parenthesis.
955-
/// Handles patterns like: `func_name`, `jet::add_32`, `fold::<f, 8>`
956-
fn extract_function_name(text: &str) -> Option<String> {
957-
let trimmed = text.trim_end();
958-
959-
// Skip generic parameters if present (e.g., `fold::<f, 8>`)
960-
let without_generics = if trimmed.ends_with('>') {
961-
let mut depth = 0;
962-
let mut start = None;
963-
for (i, ch) in trimmed.chars().rev().enumerate() {
964-
match ch {
965-
'>' => depth += 1,
966-
'<' => {
967-
depth -= 1;
968-
if depth == 0 {
969-
start = Some(trimmed.len() - 1 - i);
970-
break;
971-
}
972-
}
973-
_ => {}
974-
}
975-
}
976-
match start {
977-
Some(pos) => {
978-
let before = &trimmed[..pos];
979-
// Remove the `::` before `<` if present
980-
before.strip_suffix("::").unwrap_or(before)
981-
}
982-
None => trimmed,
983-
}
984-
} else {
985-
trimmed
986-
};
987-
988-
// Now find the function name - it should be an identifier possibly with `::`
989-
let mut name_chars = Vec::new();
990-
991-
for ch in without_generics.chars().rev() {
992-
if ch.is_alphanumeric() || ch == '_' || ch == ':' {
993-
name_chars.push(ch);
994-
} else {
995-
break;
996-
}
997-
}
998-
999-
if name_chars.is_empty() {
1000-
return None;
1001-
}
1002-
1003-
name_chars.reverse();
1004-
let name: String = name_chars.into_iter().collect();
1005-
1006-
// Clean up leading colons
1007-
let cleaned = name.trim_start_matches(':');
1008-
if cleaned.is_empty() {
1009-
None
1010-
} else {
1011-
Some(cleaned.to_string())
1012-
}
1013-
}
1014-
1015-
/// Create SignatureInformation from a FunctionTemplate.
1016-
fn create_signature_info(template: &completion::types::FunctionTemplate) -> SignatureInformation {
1017-
let params: Vec<ParameterInformation> = template
1018-
.args
1019-
.iter()
1020-
.map(|arg| ParameterInformation {
1021-
label: ParameterLabel::Simple(arg.clone()),
1022-
documentation: None,
1023-
})
1024-
.collect();
1025-
1026-
let signature_label = format!(
1027-
"fn {}({}) -> {}",
1028-
template.display_name,
1029-
template.args.join(", "),
1030-
template.return_type
1031-
);
1032-
1033-
SignatureInformation {
1034-
label: signature_label,
1035-
documentation: if template.description.is_empty() {
1036-
None
1037-
} else {
1038-
Some(tower_lsp_server::lsp_types::Documentation::MarkupContent(
1039-
MarkupContent {
1040-
kind: MarkupKind::Markdown,
1041-
value: template.description.clone(),
1042-
},
1043-
))
1044-
},
1045-
parameters: Some(params),
1046-
active_parameter: None,
1047-
}
1048-
}
1049-
1050-
/// Find signature for builtin functions.
1051-
fn find_builtin_signature(name: &str) -> Option<SignatureInformation> {
1052-
use simplicityhl::parse::CallName;
1053-
use simplicityhl::str::AliasName;
1054-
use simplicityhl::types::AliasedType;
1055-
1056-
let ty = AliasedType::from(AliasName::from_str_unchecked("T"));
1057-
1058-
// Match common builtin function names
1059-
let call_name = match name {
1060-
"unwrap_left" => Some(CallName::UnwrapLeft(ty.clone())),
1061-
"unwrap_right" => Some(CallName::UnwrapRight(ty.clone())),
1062-
"unwrap" => Some(CallName::Unwrap),
1063-
"is_none" => Some(CallName::IsNone(ty.clone())),
1064-
"assert!" => Some(CallName::Assert),
1065-
"panic!" => Some(CallName::Panic),
1066-
"dbg!" => Some(CallName::Debug),
1067-
_ => None,
1068-
};
1069-
1070-
let call_name = call_name?;
1071-
let template = completion::builtin::match_callname(&call_name)?;
1072-
Some(create_signature_info(&template))
1073-
}
1074-
1075857
#[cfg(test)]
1076858
mod tests {
1077859
use super::*;
@@ -1118,78 +900,4 @@ mod tests {
1118900
);
1119901
assert!(doc.is_none(), "Expected no document to return");
1120902
}
1121-
1122-
#[test]
1123-
fn test_extract_function_name() {
1124-
// Simple function name
1125-
assert_eq!(extract_function_name("foo"), Some("foo".to_string()));
1126-
assert_eq!(
1127-
extract_function_name("my_func"),
1128-
Some("my_func".to_string())
1129-
);
1130-
1131-
// With module prefix
1132-
assert_eq!(
1133-
extract_function_name("jet::add_32"),
1134-
Some("jet::add_32".to_string())
1135-
);
1136-
1137-
// With generic parameters
1138-
assert_eq!(
1139-
extract_function_name("fold::<f, 8>"),
1140-
Some("fold".to_string())
1141-
);
1142-
assert_eq!(
1143-
extract_function_name("unwrap_left::<u8>"),
1144-
Some("unwrap_left".to_string())
1145-
);
1146-
1147-
// With leading whitespace/expressions
1148-
assert_eq!(
1149-
extract_function_name("let x = foo"),
1150-
Some("foo".to_string())
1151-
);
1152-
1153-
// Empty input
1154-
assert_eq!(extract_function_name(""), None);
1155-
}
1156-
1157-
#[test]
1158-
fn test_find_function_call_context() {
1159-
// Simple function call
1160-
assert_eq!(
1161-
find_function_call_context("foo("),
1162-
Some(("foo".to_string(), 0))
1163-
);
1164-
assert_eq!(
1165-
find_function_call_context("foo(a, "),
1166-
Some(("foo".to_string(), 1))
1167-
);
1168-
assert_eq!(
1169-
find_function_call_context("foo(a, b, "),
1170-
Some(("foo".to_string(), 2))
1171-
);
1172-
1173-
// Nested function calls
1174-
assert_eq!(
1175-
find_function_call_context("foo(bar(x), "),
1176-
Some(("foo".to_string(), 1))
1177-
);
1178-
1179-
// Jet function
1180-
assert_eq!(
1181-
find_function_call_context("jet::add_32(a, "),
1182-
Some(("jet::add_32".to_string(), 1))
1183-
);
1184-
1185-
// With generic parameters
1186-
assert_eq!(
1187-
find_function_call_context("fold::<f, 8>(list, "),
1188-
Some(("fold".to_string(), 1))
1189-
);
1190-
1191-
// No function call context
1192-
assert_eq!(find_function_call_context("let x = 5"), None);
1193-
assert_eq!(find_function_call_context(""), None);
1194-
}
1195903
}

0 commit comments

Comments
 (0)