Add autocomplete for associated functions and struct fields

This commit is contained in:
Sofia 2025-08-03 00:13:53 +03:00
parent bb9f69ee53
commit 4ea0913842
9 changed files with 138 additions and 37 deletions

View File

@ -21,7 +21,5 @@ fn main() -> u32 {
let mut a = &mut value;
*a.second[2] = 5;
return *a.second[2];
return *a.second[0];
}

View File

@ -36,6 +36,7 @@ export function activate(context: ExtensionContext) {
env: {
...process.env,
RUST_LOG: "debug",
RUST_BACKTRACE: 1,
}
}
};

View File

@ -1,11 +1,12 @@
use std::{collections::HashMap, fmt::format, path::PathBuf};
use reid::{
ast::{self, FunctionDefinition, lexer::FullToken},
ast::{self, FunctionDefinition, lexer::FullToken, token_stream::TokenRange},
compile_module,
error_raporting::{ErrorModules, ReidError},
mir::{
self, Context, FunctionCall, FunctionParam, IfExpression, SourceModuleId, StructType, TypeKind, WhileStatement,
typecheck::typerefs::TypeRefs,
},
perform_all_passes,
};
@ -34,6 +35,7 @@ pub struct Autocomplete {
#[derive(Debug, Clone)]
pub enum AutocompleteKind {
Type,
Field(TypeKind),
Function(Vec<FunctionParam>, TypeKind),
}
@ -48,6 +50,7 @@ impl ToString for AutocompleteKind {
.collect::<Vec<_>>();
format!("({}) -> {}", params.join(", "), ret_ty)
}
AutocompleteKind::Field(type_kind) => format!("{}", type_kind),
}
}
}
@ -97,19 +100,17 @@ pub fn init_types(map: &mut TokenAnalysisMap, meta: &mir::Metadata, ty: Option<T
}
}
pub fn set_autocomplete(map: &mut TokenAnalysisMap, meta: &mir::Metadata, autocomplete: Vec<Autocomplete>) {
for token in meta.range.start..=meta.range.end {
if let Some(token) = map.get_mut(&token) {
token.autocomplete = autocomplete.clone();
} else {
map.insert(
token,
SemanticAnalysis {
ty: None,
autocomplete: autocomplete.clone(),
},
);
}
pub fn set_autocomplete(map: &mut TokenAnalysisMap, token_idx: usize, autocomplete: Vec<Autocomplete>) {
if let Some(token) = map.get_mut(&token_idx) {
token.autocomplete = autocomplete.clone();
} else {
map.insert(
token_idx,
SemanticAnalysis {
ty: None,
autocomplete: autocomplete.clone(),
},
);
}
}
@ -152,8 +153,7 @@ pub fn analyze_context(context: &mir::Context, module: &mir::Module, error: Opti
}
}
}
dbg!(import_meta, &autocompletes);
set_autocomplete(&mut map, &import_meta, autocompletes);
set_autocomplete(&mut map, import_meta.range.end, autocompletes);
}
}
@ -219,7 +219,7 @@ pub fn analyze_block(
map,
&named_variable_ref.2,
expression
.return_type(&Default::default(), source_module.module_id)
.return_type(&TypeRefs::unknown(), source_module.module_id)
.ok()
.map(|(_, ty)| ty),
);
@ -254,7 +254,7 @@ pub fn analyze_expr(
init_types(
map,
&expr.1,
expr.return_type(&Default::default(), source_module.module_id)
expr.return_type(&TypeRefs::unknown(), source_module.module_id)
.ok()
.map(|(_, t)| t),
);
@ -265,8 +265,48 @@ pub fn analyze_expr(
analyze_expr(context, source_module, &value, map);
analyze_expr(context, source_module, &index_expr, map);
}
mir::ExprKind::Accessed(expression, ..) => {
mir::ExprKind::Accessed(expression, _, name, meta) => {
analyze_expr(context, source_module, &expression, map);
let accessed_type = expression.return_type(&TypeRefs::unknown(), source_module.module_id);
let mut autocompletes = Vec::new();
match accessed_type {
Ok((_, accessed_type)) => {
autocompletes.extend(
source_module
.associated_functions
.iter()
.filter(|(t, fun)| *t == accessed_type && fun.name.starts_with(name))
.map(|(_, fun)| Autocomplete {
text: fun.name.clone(),
kind: AutocompleteKind::Function(fun.parameters.clone(), fun.return_type.clone()),
}),
);
match accessed_type {
TypeKind::CustomType(ty_key) => {
let typedef = source_module
.typedefs
.iter()
.find(|t| t.name == ty_key.0 && t.source_module == ty_key.1);
if let Some(typedef) = typedef {
autocompletes.extend(match &typedef.kind {
mir::TypeDefinitionKind::Struct(StructType(fields)) => {
fields.iter().filter(|f| f.0.starts_with(name)).map(|f| Autocomplete {
text: f.0.clone(),
kind: AutocompleteKind::Field(f.1.clone()),
})
}
});
}
}
_ => {}
}
}
_ => {}
}
set_autocomplete(map, meta.range.end - 1, autocompletes);
}
mir::ExprKind::Array(expressions) => {
for expr in expressions {
@ -288,10 +328,25 @@ pub fn analyze_expr(
analyze_expr(context, source_module, expr, map);
}
}
mir::ExprKind::AssociatedFunctionCall(_, FunctionCall { parameters, .. }) => {
mir::ExprKind::AssociatedFunctionCall(
ty,
FunctionCall {
parameters, name, meta, ..
},
) => {
for expr in parameters {
analyze_expr(context, source_module, expr, map);
}
let function_autocomplete = source_module
.associated_functions
.iter()
.filter(|(t, fun)| t == ty && fun.name.starts_with(name))
.map(|(_, fun)| Autocomplete {
text: fun.name.clone(),
kind: AutocompleteKind::Function(fun.parameters.clone(), fun.return_type.clone()),
})
.collect::<Vec<_>>();
set_autocomplete(map, meta.range.end, function_autocomplete);
}
mir::ExprKind::If(IfExpression(cond, then_e, else_e)) => {
analyze_expr(context, source_module, &cond, map);

View File

@ -175,7 +175,36 @@ impl Parse for AssociatedFunctionCall {
let ty = stream.parse()?;
stream.expect(Token::Colon)?;
stream.expect(Token::Colon)?;
Ok(AssociatedFunctionCall(ty, stream.parse()?))
match stream.parse() {
Ok(fn_call) => Ok(AssociatedFunctionCall(ty, fn_call)),
_ => {
if let Some(Token::Identifier(fn_name)) = stream.peek() {
stream.next();
stream.expected_err_nonfatal("associated function call");
Ok(AssociatedFunctionCall(
ty,
FunctionCallExpression {
name: fn_name,
params: Vec::new(),
range: stream.get_range_prev_single().unwrap(),
is_macro: false,
},
))
} else {
stream.expected_err_nonfatal("associated function name");
Ok(AssociatedFunctionCall(
ty,
FunctionCallExpression {
name: String::new(),
params: Vec::new(),
range: stream.get_range_prev_single().unwrap(),
is_macro: false,
},
))
}
}
}
}
}
@ -610,7 +639,7 @@ impl Parse for LetStatement {
stream.expect(Token::Equals)?;
let expression = stream.parse()?;
stream.expect_nonfatal(Token::Semi);
stream.expect_nonfatal(Token::Semi).ok();
Ok(LetStatement {
name: variable,
ty,
@ -645,7 +674,7 @@ impl Parse for ImportStatement {
Err(stream.expected_err("identifier")?)?
}
stream.expect_nonfatal(Token::Semi);
stream.expect_nonfatal(Token::Semi).ok();
Ok(ImportStatement(import_list, stream.get_range().unwrap()))
}
@ -934,7 +963,7 @@ impl Parse for BlockLevelStatement {
Some(Token::ReturnKeyword) => {
stream.next();
let exp = stream.parse().ok();
stream.expect(Token::Semi)?;
stream.expect_nonfatal(Token::Semi).ok();
Stmt::Return(ReturnType::Hard, exp)
}
Some(Token::For) => {
@ -999,7 +1028,7 @@ impl Parse for SetStatement {
let var_ref = stream.parse()?;
stream.expect(Token::Equals)?;
let expr = stream.parse()?;
stream.expect_nonfatal(Token::Semi);
stream.expect_nonfatal(Token::Semi).ok();
Ok(SetStatement(var_ref, expr, stream.get_range().unwrap()))
}
}
@ -1040,7 +1069,7 @@ impl Parse for TopLevelStatement {
stream.next(); // Consume Extern
stream.expect(Token::FnKeyword)?;
let extern_fn = Stmt::ExternFunction(stream.parse()?);
stream.expect_nonfatal(Token::Semi);
stream.expect_nonfatal(Token::Semi).ok();
extern_fn
}
Some(Token::FnKeyword) | Some(Token::PubKeyword) => Stmt::FunctionDefinition(stream.parse()?),

View File

@ -87,15 +87,18 @@ impl<'a, 'b> TokenStream<'a, 'b> {
}
}
pub fn expect_nonfatal(&mut self, token: Token) {
pub fn expect_nonfatal(&mut self, token: Token) -> Result<(), ()> {
if let (pos, Some(peeked)) = self.next_token(self.position) {
if token == peeked.token {
self.position = pos + 1;
Ok(())
} else {
self.expecting_err_nonfatal(token);
Err(())
}
} else {
self.expecting_err_nonfatal(token);
Err(())
}
}

View File

@ -130,7 +130,7 @@ pub fn compile_module<'map>(
};
if errors.len() > 0 {
dbg!(&ast_module);
// dbg!(&ast_module);
return Ok(Err((
ast_module,
ReidError::from_kind(

View File

@ -621,7 +621,7 @@ impl Expression {
// Update possibly resolved type
Ok(true_ty)
} else {
Err(ErrorKind::NoSuchField(field_name.clone()))
Err(ErrorKind::NoSuchField(key.0.clone()))
}
} else {
Err(ErrorKind::TriedAccessingNonStruct(expr_ty))

View File

@ -14,7 +14,7 @@ use crate::{
mir::{
pass::{AssociatedFunctionKey, ScopeVariable},
BinopDefinition, Block, CustomTypeKey, ExprKind, Expression, FunctionDefinition, FunctionDefinitionKind,
IfExpression, Module, ReturnKind, StmtKind, TypeKind, WhileStatement,
IfExpression, Module, ReturnKind, StmtKind, TypeKind, VagueType, WhileStatement,
},
util::try_all,
};
@ -546,10 +546,10 @@ impl Expression {
*type_kind = elem_ty.as_type().clone();
Ok(elem_ty)
}
None => Err(ErrorKind::NoSuchField(field_name.clone())),
None => Ok(type_refs.from_type(&TypeKind::Vague(VagueType::Unknown)).unwrap()),
}
}
_ => Err(ErrorKind::TriedAccessingNonStruct(kind)),
_ => Ok(type_refs.from_type(&TypeKind::Vague(VagueType::Unknown)).unwrap()),
}
}
ExprKind::Struct(struct_name, fields) => {

View File

@ -97,6 +97,9 @@ pub struct TypeRefs {
/// Indirect ID-references, referring to hints-vec
pub(super) type_refs: RefCell<Vec<TypeIdRef>>,
pub(super) binop_types: BinopMap,
/// Used when the real typerefs are not available, and any TypeRefs need to
/// be resolved as Unknown.
pub unknown_typerefs: bool,
}
impl std::fmt::Display for TypeRefs {
@ -122,6 +125,14 @@ impl TypeRefs {
hints: Default::default(),
type_refs: Default::default(),
binop_types: binops,
unknown_typerefs: false,
}
}
pub fn unknown() -> TypeRefs {
TypeRefs {
unknown_typerefs: true,
..Default::default()
}
}
@ -177,8 +188,12 @@ impl TypeRefs {
}
pub fn retrieve_typeref(&self, idx: usize) -> Option<TypeRefKind> {
let inner_idx = unsafe { *self.recurse_type_ref(idx).borrow() };
self.hints.borrow().get(inner_idx).cloned()
if !self.unknown_typerefs {
let inner_idx = unsafe { *self.recurse_type_ref(idx).borrow() };
self.hints.borrow().get(inner_idx).cloned()
} else {
Some(TypeRefKind::Direct(TypeKind::Vague(VagueType::Unknown)))
}
}
pub fn retrieve_wide_type(&self, idx: usize, seen: &mut HashSet<usize>) -> Option<TypeKind> {