From c9b44dcb1b822d213d71b637bb0033258d6b8216 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 17 Nov 2025 09:14:06 +0800 Subject: [PATCH 1/8] Completion kind --- crates/rue-compiler/src/compiler.rs | 14 +- crates/rue-lsp/src/cache.rs | 212 ++++++++++++++++++++-------- crates/rue-lsp/src/main.rs | 11 +- crates/rue-types/src/extractors.rs | 3 +- 4 files changed, 180 insertions(+), 60 deletions(-) diff --git a/crates/rue-compiler/src/compiler.rs b/crates/rue-compiler/src/compiler.rs index d14b9d70..0ba479d0 100644 --- a/crates/rue-compiler/src/compiler.rs +++ b/crates/rue-compiler/src/compiler.rs @@ -255,7 +255,12 @@ impl Compiler { } pub fn type_name(&mut self, ty: TypeId) -> String { - let mut current = Some(self.last_scope_id()); + let current = self.last_scope_id(); + self.type_name_in(current, ty) + } + + pub fn type_name_in(&mut self, scope: ScopeId, ty: TypeId) -> String { + let mut current = Some(scope); while let Some(scope) = current { if let Some(name) = self.scope(scope).type_name(ty) { @@ -284,7 +289,12 @@ impl Compiler { } pub fn symbol_type(&self, symbol: SymbolId) -> TypeId { - let mut current = Some(self.last_scope_id()); + let current = self.last_scope_id(); + self.symbol_type_in(current, symbol) + } + + pub fn symbol_type_in(&self, scope: ScopeId, symbol: SymbolId) -> TypeId { + let mut current = Some(scope); while let Some(scope) = current { if let Some(ty) = self.scope(scope).symbol_override_type(symbol) { diff --git a/crates/rue-lsp/src/cache.rs b/crates/rue-lsp/src/cache.rs index 2e71a14a..be0aa900 100644 --- a/crates/rue-lsp/src/cache.rs +++ b/crates/rue-lsp/src/cache.rs @@ -1,12 +1,14 @@ use std::{collections::HashSet, sync::Arc}; -use indexmap::IndexSet; +use indexmap::IndexMap; use rowan::{TextRange, TextSize}; use rue_compiler::{Compiler, CompletionContext, SyntaxItemKind, SyntaxMap}; use rue_diagnostic::{LineCol, Source}; use rue_hir::{ScopeId, Symbol, SymbolId}; -use rue_types::{Type, TypeId}; -use tower_lsp::lsp_types::{CompletionItem, Position, Range}; +use rue_types::{Type, TypeId, Union}; +use tower_lsp::lsp_types::{ + CompletionItem, CompletionItemKind, CompletionItemLabelDetails, Position, Range, +}; #[derive(Debug, Clone)] pub struct Scopes(Vec); @@ -16,6 +18,7 @@ pub enum HoverInfo { Symbol(SymbolHoverInfo), Module(ModuleHoverInfo), Type(TypeHoverInfo), + Struct(StructHoverInfo), Field(FieldHoverInfo), } @@ -36,6 +39,12 @@ pub struct TypeHoverInfo { pub inner_name: Option, } +#[derive(Debug, Clone)] +pub struct StructHoverInfo { + pub name: String, + pub fields: Vec, +} + #[derive(Debug, Clone)] pub struct FieldHoverInfo { pub name: String, @@ -148,27 +157,50 @@ impl Cache { { let semantic = rue_types::unwrap_semantic(self.ctx.types_mut(), ty, true); - let mut fields = IndexSet::new(); + let mut fields = IndexMap::new(); match self.ctx.ty(semantic) { - Type::Struct(ty) => { - fields.extend(ty.fields.clone()); + Type::Struct(_) => { + for field in self.struct_fields(scopes, semantic).unwrap() { + fields.insert(field.name, field.type_name); + } } Type::Atom(_) => { - fields.insert("length".to_string()); + let ty = self.ctx.builtins().types.int; + let type_name = self.type_name(scopes, ty); + fields.insert("length".to_string(), type_name); } Type::Pair(_) | Type::Union(_) => { let pairs = rue_types::extract_pairs(self.ctx.types_mut(), ty, true); if !pairs.is_empty() { - fields.insert("first".to_string()); - fields.insert("rest".to_string()); + let first = if pairs.len() == 1 { + pairs[0].first + } else { + self.ctx.alloc_type(Type::Union(Union::new( + pairs.iter().map(|pair| pair.first).collect(), + ))) + }; + + let rest = if pairs.len() == 1 { + pairs[0].rest + } else { + self.ctx.alloc_type(Type::Union(Union::new( + pairs.iter().map(|pair| pair.rest).collect(), + ))) + }; + + let first = self.type_name(scopes, first); + let rest = self.type_name(scopes, rest); + + fields.insert("first".to_string(), first); + fields.insert("rest".to_string(), rest); } } _ => {} } - for field in fields { + for (field, field_type) in fields { if specified_fields .as_ref() .is_some_and(|fields| fields.contains(&field)) @@ -179,7 +211,11 @@ impl Cache { if let Some(score) = fuzzy_match(&partial, &field) { scored_items.push(( score, - CompletionItem::new_simple(field, "Field".to_string()), + create_completion_item( + field, + Some(CompletionItemKind::FIELD), + Some(field_type), + ), )); } } @@ -207,10 +243,22 @@ impl Cache { } if let Some(score) = fuzzy_match(&partial, &name) { - scored_items.push(( - score, - CompletionItem::new_simple(name, "Symbol".to_string()), - )); + let symbol = self.ctx.scope(*scope).symbol(&name).unwrap(); + let ty = self.ctx.symbol_type_in(*scope, symbol); + let type_name = self.type_name(scopes, ty); + + let kind = match self.ctx.symbol(symbol) { + Symbol::Binding(_) => Some(CompletionItemKind::VARIABLE), + Symbol::Constant(_) => Some(CompletionItemKind::CONSTANT), + Symbol::Function(_) => Some(CompletionItemKind::FUNCTION), + Symbol::Parameter(_) => Some(CompletionItemKind::VARIABLE), + Symbol::Module(_) => Some(CompletionItemKind::MODULE), + Symbol::Builtin(_) => Some(CompletionItemKind::FUNCTION), + Symbol::Unresolved => None, + }; + + scored_items + .push((score, create_completion_item(name, kind, Some(type_name)))); } } } @@ -241,8 +289,13 @@ impl Cache { } if let Some(score) = fuzzy_match(&partial, &name) { - scored_items - .push((score, CompletionItem::new_simple(name, "Type".to_string()))); + let ty = self.ctx.scope(*scope).ty(&name).unwrap(); + let type_name = self.inner_type(ty).map(|ty| self.type_name(scopes, ty)); + + scored_items.push(( + score, + create_completion_item(name, Some(CompletionItemKind::STRUCT), type_name), + )); } } } @@ -254,16 +307,22 @@ impl Cache { scored_items.into_iter().map(|(_, item)| item).collect() } - pub fn hover(&self, scopes: &Scopes, index: usize) -> Vec { + pub fn hover(&mut self, scopes: &Scopes, index: usize) -> Vec { let mut infos = Vec::new(); - for item in self.syntax_map.items() { + for item in self + .syntax_map + .items() + .map(Clone::clone) + .collect::>() + { if !contains(item.span, index) { continue; } match item.kind.clone() { - SyntaxItemKind::SymbolDeclaration(symbol) => { + SyntaxItemKind::SymbolDeclaration(symbol) + | SyntaxItemKind::SymbolReference(symbol) => { let Some(name) = self.symbol_name(scopes, symbol) else { continue; }; @@ -277,45 +336,24 @@ impl Cache { })); } } - SyntaxItemKind::SymbolReference(symbol) => { - let Some(name) = self.symbol_name(scopes, symbol) else { - continue; - }; + SyntaxItemKind::TypeDeclaration(ty) | SyntaxItemKind::TypeReference(ty) => { + let fields = self.struct_fields(scopes, ty); - if let Symbol::Module(_) = self.ctx.symbol(symbol) { - infos.push(HoverInfo::Module(ModuleHoverInfo { name })); + if let Some(fields) = fields { + infos.push(HoverInfo::Struct(StructHoverInfo { + name: self.type_name(scopes, ty), + fields, + })); } else { - infos.push(HoverInfo::Symbol(SymbolHoverInfo { - name, - type_name: self.type_name(scopes, self.symbol_type(scopes, symbol)), + infos.push(HoverInfo::Type(TypeHoverInfo { + name: self.type_name(scopes, ty), + inner_name: self.inner_type(ty).map(|ty| self.type_name(scopes, ty)), })); } } - SyntaxItemKind::TypeDeclaration(ty) => { - infos.push(HoverInfo::Type(TypeHoverInfo { - name: self.type_name(scopes, ty), - inner_name: self.inner_type(ty).map(|ty| self.type_name(scopes, ty)), - })); - } - SyntaxItemKind::TypeReference(ty) => { - infos.push(HoverInfo::Type(TypeHoverInfo { - name: self.type_name(scopes, ty), - inner_name: self.inner_type(ty).map(|ty| self.type_name(scopes, ty)), - })); - } - SyntaxItemKind::FieldDeclaration(field) => { - infos.push(HoverInfo::Field(FieldHoverInfo { - name: field.name, - type_name: self.type_name(scopes, field.ty), - })); - } - SyntaxItemKind::FieldReference(field) => { - infos.push(HoverInfo::Field(FieldHoverInfo { - name: field.name, - type_name: self.type_name(scopes, field.ty), - })); - } - SyntaxItemKind::FieldInitializer(field) => { + SyntaxItemKind::FieldDeclaration(field) + | SyntaxItemKind::FieldReference(field) + | SyntaxItemKind::FieldInitializer(field) => { infos.push(HoverInfo::Field(FieldHoverInfo { name: field.name, type_name: self.type_name(scopes, field.ty), @@ -550,7 +588,7 @@ impl Cache { Vec::new() } - fn type_name(&self, scopes: &Scopes, ty: TypeId) -> String { + fn type_name(&mut self, scopes: &Scopes, ty: TypeId) -> String { for &scope in &scopes.0 { let scope = self.ctx.scope(scope).clone(); @@ -564,7 +602,7 @@ impl Cache { Type::Alias(ty) => ty.name.as_ref().map(|token| token.text().to_string()), _ => None, } - .unwrap_or_else(|| rue_types::stringify_without_substitution(self.ctx.types(), ty)) + .unwrap_or_else(|| rue_types::stringify(self.ctx.types_mut(), ty)) } fn symbol_name(&self, scopes: &Scopes, symbol: SymbolId) -> Option { @@ -600,6 +638,52 @@ impl Cache { _ => None, } } + + fn struct_fields(&mut self, scopes: &Scopes, ty: TypeId) -> Option> { + let semantic = rue_types::unwrap_semantic(self.ctx.types_mut(), ty, true); + + let mut fields = Vec::new(); + + let Type::Struct(ty) = self.ctx.ty(semantic).clone() else { + return None; + }; + + let mut inner = ty.inner; + + for (i, name) in ty.fields.iter().enumerate() { + let next = if i == ty.fields.len() - 1 && !ty.nil_terminated { + inner + } else { + let pairs = rue_types::extract_pairs(self.ctx.types_mut(), inner, false); + + let (firsts, rests) = if pairs.is_empty() { + let never = self.ctx.builtins().types.never; + (never, never) + } else if pairs.len() == 1 { + (pairs[0].first, pairs[0].rest) + } else { + let firsts = self.ctx.alloc_type(Type::Union(Union::new( + pairs.iter().map(|pair| pair.first).collect(), + ))); + let rests = self.ctx.alloc_type(Type::Union(Union::new( + pairs.iter().map(|pair| pair.rest).collect(), + ))); + (firsts, rests) + }; + + inner = rests; + + firsts + }; + + fields.push(FieldHoverInfo { + name: name.to_string(), + type_name: self.type_name(scopes, next), + }); + } + + Some(fields) + } } fn contains(range: TextRange, index: usize) -> bool { @@ -664,3 +748,19 @@ fn fuzzy_match(query: &str, candidate: &str) -> Option { Some(consecutive_score + match_ratio_score + position_score) } + +fn create_completion_item( + name: String, + kind: Option, + ty: Option, +) -> CompletionItem { + CompletionItem { + label: name, + kind, + label_details: Some(CompletionItemLabelDetails { + detail: ty.map(|ty| format!(" {ty}")), + description: None, + }), + ..Default::default() + } +} diff --git a/crates/rue-lsp/src/main.rs b/crates/rue-lsp/src/main.rs index 0642e822..b6071892 100644 --- a/crates/rue-lsp/src/main.rs +++ b/crates/rue-lsp/src/main.rs @@ -183,7 +183,7 @@ impl Backend { } fn on_hover(&self, params: &HoverParams) -> Option { - let cache = self + let mut cache = self .cache .lock() .unwrap() @@ -213,6 +213,15 @@ impl Backend { format!("type {}", info.name) } } + HoverInfo::Struct(info) => format!( + "struct {} {{\n{}\n}}", + info.name, + info.fields + .iter() + .map(|field| format!(" {}: {},", field.name, field.type_name)) + .collect::>() + .join("\n") + ), HoverInfo::Field(info) => format!("{}: {}", info.name, info.type_name), }, }) diff --git a/crates/rue-types/src/extractors.rs b/crates/rue-types/src/extractors.rs index 3689b586..9054c857 100644 --- a/crates/rue-types/src/extractors.rs +++ b/crates/rue-types/src/extractors.rs @@ -67,7 +67,8 @@ fn extract_pairs_impl(arena: &Arena, id: TypeId, strict: bool) -> Option unreachable!(), Type::Ref(id) => extract_pairs_impl(arena, id, strict), Type::Unresolved => Some(vec![]), - Type::Generic(_) | Type::Never | Type::Atom(_) | Type::Function(_) | Type::Any => None, + Type::Generic(_) | Type::Atom(_) | Type::Function(_) | Type::Any => None, + Type::Never => Some(vec![]), Type::Pair(pair) => Some(vec![pair]), Type::Struct(ty) => extract_pairs_impl(arena, ty.inner, strict), Type::Alias(alias) => extract_pairs_impl(arena, alias.inner, strict), From 12392729f663b039417e20c8cc3bc32b4adaf4df Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 17 Nov 2025 09:38:28 +0800 Subject: [PATCH 2/8] Cross file goto references/definitions --- .../src/compile/binding/struct_binding.rs | 12 +-- crates/rue-compiler/src/compile/block.rs | 11 ++- crates/rue-compiler/src/compile/expr.rs | 6 +- .../src/compile/expr/field_access.rs | 12 +-- .../src/compile/expr/struct_initializer.rs | 12 +-- crates/rue-compiler/src/compile/imports.rs | 71 ++++++++-------- .../rue-compiler/src/compile/item/constant.rs | 6 +- .../rue-compiler/src/compile/item/function.rs | 8 +- .../rue-compiler/src/compile/item/module.rs | 8 +- .../src/compile/item/struct_item.rs | 10 +-- .../src/compile/item/type_alias.rs | 7 +- crates/rue-compiler/src/compile/path.rs | 7 +- crates/rue-compiler/src/compile/ty.rs | 6 +- crates/rue-compiler/src/compiler.rs | 37 ++++---- crates/rue-compiler/src/file.rs | 9 +- crates/rue-compiler/src/syntax_map.rs | 10 ++- crates/rue-lsp/src/cache.rs | 84 ++++++++++++------- crates/rue-lsp/src/main.rs | 26 ++---- 18 files changed, 175 insertions(+), 167 deletions(-) diff --git a/crates/rue-compiler/src/compile/binding/struct_binding.rs b/crates/rue-compiler/src/compile/binding/struct_binding.rs index ac31736e..7205b99d 100644 --- a/crates/rue-compiler/src/compile/binding/struct_binding.rs +++ b/crates/rue-compiler/src/compile/binding/struct_binding.rs @@ -6,8 +6,8 @@ use rue_diagnostic::DiagnosticKind; use rue_hir::{BindingSymbol, Declaration, Hir, Symbol, SymbolId, SymbolPath, Value}; use crate::{ - Compiler, CompletionContext, Field, FieldResult, SyntaxField, SyntaxItem, SyntaxItemKind, - compile_field, create_binding, create_binding_for_identifier, + Compiler, CompletionContext, Field, FieldResult, SyntaxField, SyntaxItemKind, compile_field, + create_binding, create_binding_for_identifier, }; pub fn create_struct_binding( @@ -70,21 +70,21 @@ pub fn create_struct_binding( ctx.pop_declaration(); - ctx.syntax_map_mut().add_item(SyntaxItem::new( + ctx.add_syntax( SyntaxItemKind::FieldReference(SyntaxField { name: name.text().to_string(), container: ty, ty: field_type, }), name.text_range(), - )); + ); } - ctx.syntax_map_mut().add_item(SyntaxItem::new( + ctx.add_syntax( SyntaxItemKind::CompletionContext(CompletionContext::StructFields { ty, specified_fields: Some(specified_fields), }), struct_binding.syntax().text_range(), - )); + ); } diff --git a/crates/rue-compiler/src/compile/block.rs b/crates/rue-compiler/src/compile/block.rs index 66f8b27e..25ee3e62 100644 --- a/crates/rue-compiler/src/compile/block.rs +++ b/crates/rue-compiler/src/compile/block.rs @@ -7,8 +7,7 @@ use rue_hir::{ use rue_types::{Type, TypeId, Union}; use crate::{ - Compiler, CompletionContext, SyntaxItem, SyntaxItemKind, compile_expr, compile_type, - create_binding, + Compiler, CompletionContext, SyntaxItemKind, compile_expr, compile_type, create_binding, }; pub fn compile_block( @@ -18,10 +17,10 @@ pub fn compile_block( expected_type: Option, require_return: bool, ) -> Value { - ctx.syntax_map_mut().add_item(SyntaxItem::new( + ctx.add_syntax( SyntaxItemKind::CompletionContext(CompletionContext::Expression), block.syntax().text_range(), - )); + ); let index = ctx.mapping_checkpoint(); @@ -37,10 +36,10 @@ pub fn compile_block( for stmt in block.items() { let stmt = match stmt { AstStmtOrExpr::Stmt(stmt) => { - ctx.syntax_map_mut().add_item(SyntaxItem::new( + ctx.add_syntax( SyntaxItemKind::CompletionContext(CompletionContext::Statement), stmt.syntax().text_range(), - )); + ); stmt } diff --git a/crates/rue-compiler/src/compile/expr.rs b/crates/rue-compiler/src/compile/expr.rs index daba5c4a..94580060 100644 --- a/crates/rue-compiler/src/compile/expr.rs +++ b/crates/rue-compiler/src/compile/expr.rs @@ -32,13 +32,13 @@ use rue_ast::{AstExpr, AstNode}; use rue_hir::Value; use rue_types::TypeId; -use crate::{Compiler, CompletionContext, SyntaxItem, SyntaxItemKind, compile_block}; +use crate::{Compiler, CompletionContext, SyntaxItemKind, compile_block}; pub fn compile_expr(ctx: &mut Compiler, expr: &AstExpr, expected_type: Option) -> Value { - ctx.syntax_map_mut().add_item(SyntaxItem::new( + ctx.add_syntax( SyntaxItemKind::CompletionContext(CompletionContext::Expression), expr.syntax().text_range(), - )); + ); match expr { AstExpr::PathExpr(expr) => compile_path_expr(ctx, expr), diff --git a/crates/rue-compiler/src/compile/expr/field_access.rs b/crates/rue-compiler/src/compile/expr/field_access.rs index 0ec84082..6ff8a025 100644 --- a/crates/rue-compiler/src/compile/expr/field_access.rs +++ b/crates/rue-compiler/src/compile/expr/field_access.rs @@ -5,8 +5,8 @@ use rue_diagnostic::DiagnosticKind; use rue_hir::Value; use crate::{ - Compiler, CompletionContext, Field, FieldResult, SyntaxField, SyntaxItem, SyntaxItemKind, - compile_expr, compile_field, + Compiler, CompletionContext, Field, FieldResult, SyntaxField, SyntaxItemKind, compile_expr, + compile_field, }; pub fn compile_field_access_expr(ctx: &mut Compiler, access: &AstFieldAccessExpr) -> Value { @@ -20,13 +20,13 @@ pub fn compile_field_access_expr(ctx: &mut Compiler, access: &AstFieldAccessExpr ctx.builtins().unresolved.clone() }; - ctx.syntax_map_mut().add_item(SyntaxItem::new( + ctx.add_syntax( SyntaxItemKind::CompletionContext(CompletionContext::StructFields { ty: expr.ty, specified_fields: None, }), TextRange::new(start, access.syntax().text_range().end()), - )); + ); let Some(name) = access.field() else { debug!("Unresolved field access name"); @@ -35,14 +35,14 @@ pub fn compile_field_access_expr(ctx: &mut Compiler, access: &AstFieldAccessExpr match compile_field(ctx, expr.clone(), &Field::Named(name.text())) { FieldResult::Value(value) => { - ctx.syntax_map_mut().add_item(SyntaxItem::new( + ctx.add_syntax( SyntaxItemKind::FieldReference(SyntaxField { name: name.text().to_string(), container: expr.ty, ty: value.ty, }), name.text_range(), - )); + ); value } FieldResult::Unknown => { diff --git a/crates/rue-compiler/src/compile/expr/struct_initializer.rs b/crates/rue-compiler/src/compile/expr/struct_initializer.rs index 3db4b00c..d0c79e6c 100644 --- a/crates/rue-compiler/src/compile/expr/struct_initializer.rs +++ b/crates/rue-compiler/src/compile/expr/struct_initializer.rs @@ -7,8 +7,8 @@ use rue_hir::{Hir, SymbolPath, Value}; use rue_types::{Pair, Type, Union}; use crate::{ - Compiler, CompletionContext, PathKind, PathResult, SyntaxField, SyntaxItem, SyntaxItemKind, - compile_expr, compile_path, + Compiler, CompletionContext, PathKind, PathResult, SyntaxField, SyntaxItemKind, compile_expr, + compile_path, }; pub fn compile_struct_initializer_expr( @@ -27,7 +27,7 @@ pub fn compile_struct_initializer_expr( let semantic = rue_types::unwrap_semantic(ctx.types_mut(), ty, true); - ctx.syntax_map_mut().add_item(SyntaxItem::new( + ctx.add_syntax( SyntaxItemKind::CompletionContext(CompletionContext::StructFields { ty: semantic, specified_fields: Some( @@ -38,7 +38,7 @@ pub fn compile_struct_initializer_expr( ), }), expr.syntax().text_range(), - )); + ); let Type::Struct(struct_type) = ctx.ty(semantic).clone() else { debug!("Unresolved struct initializer due to non struct type"); @@ -124,7 +124,7 @@ pub fn compile_struct_initializer_expr( continue; }; - ctx.syntax_map_mut().add_item(SyntaxItem::new( + ctx.add_syntax( SyntaxItemKind::FieldInitializer(SyntaxField { name: name.text().to_string(), container: semantic, @@ -134,7 +134,7 @@ pub fn compile_struct_initializer_expr( .unwrap_or(value.ty), }), name.text_range(), - )); + ); if struct_type.fields.contains(name.text()) { if let Some(expected_type) = expected_field_types.get(name.text()) { diff --git a/crates/rue-compiler/src/compile/imports.rs b/crates/rue-compiler/src/compile/imports.rs index e1b3bf60..501ddadf 100644 --- a/crates/rue-compiler/src/compile/imports.rs +++ b/crates/rue-compiler/src/compile/imports.rs @@ -6,7 +6,7 @@ use rue_ast::{AstImportItem, AstImportPathSegment}; use rue_diagnostic::{DiagnosticKind, Name}; use rue_hir::{Declaration, Import, ImportId, Items, ScopeId, Symbol, SymbolId}; -use crate::{Compiler, SyntaxItem, SyntaxItemKind}; +use crate::{Compiler, SyntaxItemKind}; pub fn declare_import_item(ctx: &mut Compiler, import: &AstImportItem) { let Some(path) = import.path() else { @@ -49,10 +49,7 @@ fn construct_imports( } else if let Some(module) = module_stack.pop() { base_scope = ctx.module(module).scope; - ctx.syntax_map_mut().add_item(SyntaxItem::new( - SyntaxItemKind::SymbolReference(module), - name.text_range(), - )); + ctx.add_syntax(SyntaxItemKind::SymbolReference(module), name.text_range()); } else { ctx.diagnostic(&name, DiagnosticKind::UnresolvedSuper); } @@ -230,14 +227,14 @@ fn resolve_import( for name in import.path.iter().take(path_so_far.len()) { if diagnostics && let Some(srcloc) = name.srcloc() { - ctx.syntax_map_for_source(source.kind.clone()) - .add_item(SyntaxItem::new( - SyntaxItemKind::SymbolReference(cached.1), - TextRange::new( - srcloc.span.start.try_into().unwrap(), - srcloc.span.end.try_into().unwrap(), - ), - )); + ctx.add_syntax_for_source( + SyntaxItemKind::SymbolReference(cached.1), + TextRange::new( + srcloc.span.start.try_into().unwrap(), + srcloc.span.end.try_into().unwrap(), + ), + source.kind.clone(), + ); } } @@ -274,14 +271,14 @@ fn resolve_import( } if diagnostics && let Some(srcloc) = name.srcloc() { - ctx.syntax_map_for_source(source.kind.clone()) - .add_item(SyntaxItem::new( - SyntaxItemKind::SymbolReference(symbol), - TextRange::new( - srcloc.span.start.try_into().unwrap(), - srcloc.span.end.try_into().unwrap(), - ), - )); + ctx.add_syntax_for_source( + SyntaxItemKind::SymbolReference(symbol), + TextRange::new( + srcloc.span.start.try_into().unwrap(), + srcloc.span.end.try_into().unwrap(), + ), + source.kind.clone(), + ); } let Symbol::Module(module) = ctx.symbol(symbol) else { @@ -427,14 +424,14 @@ fn resolve_import( if let Some(symbol) = symbol { if diagnostics && let Some(srcloc) = item.srcloc() { - ctx.syntax_map_for_source(source.kind.clone()) - .add_item(SyntaxItem::new( - SyntaxItemKind::SymbolReference(symbol), - TextRange::new( - srcloc.span.start.try_into().unwrap(), - srcloc.span.end.try_into().unwrap(), - ), - )); + ctx.add_syntax_for_source( + SyntaxItemKind::SymbolReference(symbol), + TextRange::new( + srcloc.span.start.try_into().unwrap(), + srcloc.span.end.try_into().unwrap(), + ), + source.kind.clone(), + ); } let target = ctx.scope_mut(import_scope); @@ -462,14 +459,14 @@ fn resolve_import( if let Some(ty) = ty { if diagnostics && let Some(srcloc) = item.srcloc() { - ctx.syntax_map_for_source(source.kind.clone()) - .add_item(SyntaxItem::new( - SyntaxItemKind::TypeReference(ty), - TextRange::new( - srcloc.span.start.try_into().unwrap(), - srcloc.span.end.try_into().unwrap(), - ), - )); + ctx.add_syntax_for_source( + SyntaxItemKind::TypeReference(ty), + TextRange::new( + srcloc.span.start.try_into().unwrap(), + srcloc.span.end.try_into().unwrap(), + ), + source.kind.clone(), + ); } let target = ctx.scope_mut(import_scope); diff --git a/crates/rue-compiler/src/compile/item/constant.rs b/crates/rue-compiler/src/compile/item/constant.rs index 80a39175..994106b4 100644 --- a/crates/rue-compiler/src/compile/item/constant.rs +++ b/crates/rue-compiler/src/compile/item/constant.rs @@ -3,13 +3,13 @@ use rue_ast::{AstConstantItem, AstNode}; use rue_diagnostic::DiagnosticKind; use rue_hir::{ConstantSymbol, Declaration, Symbol, SymbolId, Value}; -use crate::{Compiler, CompletionContext, SyntaxItem, SyntaxItemKind, compile_expr, compile_type}; +use crate::{Compiler, CompletionContext, SyntaxItemKind, compile_expr, compile_type}; pub fn declare_constant(ctx: &mut Compiler, constant: &AstConstantItem) -> SymbolId { - ctx.syntax_map_mut().add_item(SyntaxItem::new( + ctx.add_syntax( SyntaxItemKind::CompletionContext(CompletionContext::Item), constant.syntax().text_range(), - )); + ); let symbol = ctx.alloc_symbol(Symbol::Unresolved); diff --git a/crates/rue-compiler/src/compile/item/function.rs b/crates/rue-compiler/src/compile/item/function.rs index b66edc6a..bc8fd8c0 100644 --- a/crates/rue-compiler/src/compile/item/function.rs +++ b/crates/rue-compiler/src/compile/item/function.rs @@ -6,15 +6,15 @@ use rue_hir::{Declaration, FunctionKind, FunctionSymbol, ParameterSymbol, Symbol use rue_types::{FunctionType, Type}; use crate::{ - Compiler, CompletionContext, SyntaxItem, SyntaxItemKind, compile_block, - compile_generic_parameters, compile_type, create_binding, + Compiler, CompletionContext, SyntaxItemKind, compile_block, compile_generic_parameters, + compile_type, create_binding, }; pub fn declare_function(ctx: &mut Compiler, function: &AstFunctionItem) -> SymbolId { - ctx.syntax_map_mut().add_item(SyntaxItem::new( + ctx.add_syntax( SyntaxItemKind::CompletionContext(CompletionContext::Item), function.syntax().text_range(), - )); + ); let symbol = ctx.alloc_symbol(Symbol::Unresolved); diff --git a/crates/rue-compiler/src/compile/item/module.rs b/crates/rue-compiler/src/compile/item/module.rs index 66cc7c6c..89513d8b 100644 --- a/crates/rue-compiler/src/compile/item/module.rs +++ b/crates/rue-compiler/src/compile/item/module.rs @@ -3,15 +3,15 @@ use rue_diagnostic::DiagnosticKind; use rue_hir::{Declaration, ModuleDeclarations, ModuleSymbol, Symbol, SymbolId}; use crate::{ - Compiler, CompletionContext, SyntaxItem, SyntaxItemKind, compile_symbol_items, - compile_type_items, declare_module_items, declare_symbol_items, declare_type_items, + Compiler, CompletionContext, SyntaxItemKind, compile_symbol_items, compile_type_items, + declare_module_items, declare_symbol_items, declare_type_items, }; pub fn declare_module(ctx: &mut Compiler, module: &AstModuleItem) -> SymbolId { - ctx.syntax_map_mut().add_item(SyntaxItem::new( + ctx.add_syntax( SyntaxItemKind::CompletionContext(CompletionContext::Item), module.syntax().text_range(), - )); + ); let scope = ctx.alloc_child_scope(); diff --git a/crates/rue-compiler/src/compile/item/struct_item.rs b/crates/rue-compiler/src/compile/item/struct_item.rs index 9ed016e8..12dc511b 100644 --- a/crates/rue-compiler/src/compile/item/struct_item.rs +++ b/crates/rue-compiler/src/compile/item/struct_item.rs @@ -6,15 +6,15 @@ use rue_hir::{Declaration, ScopeId}; use rue_types::{Pair, Struct, Type, TypeId}; use crate::{ - Compiler, CompletionContext, SyntaxField, SyntaxItem, SyntaxItemKind, compile_expr, + Compiler, CompletionContext, SyntaxField, SyntaxItemKind, compile_expr, compile_generic_parameters, compile_type, }; pub fn declare_struct_item(ctx: &mut Compiler, struct_item: &AstStructItem) -> (TypeId, ScopeId) { - ctx.syntax_map_mut().add_item(SyntaxItem::new( + ctx.add_syntax( SyntaxItemKind::CompletionContext(CompletionContext::Item), struct_item.syntax().text_range(), - )); + ); let ty = ctx.alloc_type(Type::Unresolved); @@ -140,14 +140,14 @@ pub fn compile_struct_item( types.push(field_type); } - ctx.syntax_map_mut().add_item(SyntaxItem::new( + ctx.add_syntax( SyntaxItemKind::FieldDeclaration(SyntaxField { name: name.text().to_string(), container: struct_type, ty: field_type, }), name.text_range(), - )); + ); } ctx.pop_scope(range.end()); diff --git a/crates/rue-compiler/src/compile/item/type_alias.rs b/crates/rue-compiler/src/compile/item/type_alias.rs index 37aaca51..4fd8b39d 100644 --- a/crates/rue-compiler/src/compile/item/type_alias.rs +++ b/crates/rue-compiler/src/compile/item/type_alias.rs @@ -5,15 +5,14 @@ use rue_hir::{Declaration, ScopeId}; use rue_types::{Alias, Type, TypeId}; use crate::{ - Compiler, CompletionContext, SyntaxItem, SyntaxItemKind, compile_generic_parameters, - compile_type, + Compiler, CompletionContext, SyntaxItemKind, compile_generic_parameters, compile_type, }; pub fn declare_type_alias(ctx: &mut Compiler, type_alias: &AstTypeAliasItem) -> (TypeId, ScopeId) { - ctx.syntax_map_mut().add_item(SyntaxItem::new( + ctx.add_syntax( SyntaxItemKind::CompletionContext(CompletionContext::Item), type_alias.syntax().text_range(), - )); + ); let ty = ctx.alloc_type(Type::Unresolved); diff --git a/crates/rue-compiler/src/compile/path.rs b/crates/rue-compiler/src/compile/path.rs index 57603d13..fad11ea3 100644 --- a/crates/rue-compiler/src/compile/path.rs +++ b/crates/rue-compiler/src/compile/path.rs @@ -8,7 +8,7 @@ use rue_hir::{Declaration, ImportId, Symbol, SymbolId}; use rue_parser::SyntaxToken; use rue_types::{Apply, Type, TypeId}; -use crate::{Compiler, GetTextRange, SyntaxItem, SyntaxItemKind, compile_generic_arguments}; +use crate::{Compiler, GetTextRange, SyntaxItemKind, compile_generic_arguments}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum PathKind { @@ -82,10 +82,7 @@ where if let Some(module) = module_stack.pop() { base_scope = ctx.module(module).scope; - ctx.syntax_map_mut().add_item(SyntaxItem::new( - SyntaxItemKind::SymbolReference(module), - name.text_range(), - )); + ctx.add_syntax(SyntaxItemKind::SymbolReference(module), name.text_range()); } else { ctx.diagnostic(&name, DiagnosticKind::UnresolvedSuper); } diff --git a/crates/rue-compiler/src/compile/ty.rs b/crates/rue-compiler/src/compile/ty.rs index 6dc8cda6..cf459cc5 100644 --- a/crates/rue-compiler/src/compile/ty.rs +++ b/crates/rue-compiler/src/compile/ty.rs @@ -17,13 +17,13 @@ pub use union::*; use rue_ast::{AstNode, AstType}; use rue_types::TypeId; -use crate::{Compiler, CompletionContext, SyntaxItem, SyntaxItemKind}; +use crate::{Compiler, CompletionContext, SyntaxItemKind}; pub fn compile_type(ctx: &mut Compiler, ty: &AstType) -> TypeId { - ctx.syntax_map_mut().add_item(SyntaxItem::new( + ctx.add_syntax( SyntaxItemKind::CompletionContext(CompletionContext::Type), ty.syntax().text_range(), - )); + ); match ty { AstType::PathType(path) => compile_path_type(ctx, path), diff --git a/crates/rue-compiler/src/compiler.rs b/crates/rue-compiler/src/compiler.rs index 0ba479d0..1c245c62 100644 --- a/crates/rue-compiler/src/compiler.rs +++ b/crates/rue-compiler/src/compiler.rs @@ -25,7 +25,7 @@ pub struct Compiler { source: Source, diagnostics: Vec, db: Database, - syntax_maps: HashMap, + syntax_map: SyntaxMap, scope_stack: Vec<(TextSize, ScopeId)>, module_stack: Vec, builtins: Builtins, @@ -59,7 +59,7 @@ impl Compiler { source: Source::new(Arc::from(""), SourceKind::Std), diagnostics: Vec::new(), db, - syntax_maps: HashMap::new(), + syntax_map: SyntaxMap::new(), scope_stack: vec![(TextSize::from(0), builtins.scope)], module_stack: Vec::new(), builtins, @@ -116,18 +116,22 @@ impl Compiler { self.scope_stack.iter().map(|(_, scope)| *scope).collect() } - pub fn syntax_map(&self, source_kind: &SourceKind) -> Option<&SyntaxMap> { - self.syntax_maps.get(source_kind) + pub fn syntax_map(&self) -> &SyntaxMap { + &self.syntax_map } - pub fn syntax_map_mut(&mut self) -> &mut SyntaxMap { - self.syntax_maps - .entry(self.source.kind.clone()) - .or_default() + pub fn add_syntax(&mut self, kind: SyntaxItemKind, span: TextRange) { + self.add_syntax_for_source(kind, span, self.source.kind.clone()); } - pub fn syntax_map_for_source(&mut self, source: SourceKind) -> &mut SyntaxMap { - self.syntax_maps.entry(source).or_default() + pub fn add_syntax_for_source( + &mut self, + kind: SyntaxItemKind, + span: TextRange, + source_kind: SourceKind, + ) { + self.syntax_map + .add_item(SyntaxItem::new(kind, span, source_kind)); } pub fn take_diagnostics(&mut self) -> Vec { @@ -184,10 +188,7 @@ impl Compiler { return; } - self.syntax_map_mut().add_item(SyntaxItem::new( - SyntaxItemKind::Scope(scope), - TextRange::new(start, end), - )); + self.add_syntax(SyntaxItemKind::Scope(scope), TextRange::new(start, end)); } pub fn last_scope(&self) -> &Scope { @@ -514,23 +515,23 @@ impl Compiler { } pub fn declaration_span(&mut self, declaration: Declaration, span: TextRange) { - self.syntax_map_mut().add_item(SyntaxItem::new( + self.add_syntax( match declaration { Declaration::Symbol(symbol) => SyntaxItemKind::SymbolDeclaration(symbol), Declaration::Type(ty) => SyntaxItemKind::TypeDeclaration(ty), }, span, - )); + ); } pub fn reference_span(&mut self, reference: Declaration, span: TextRange) { - self.syntax_map_mut().add_item(SyntaxItem::new( + self.add_syntax( match reference { Declaration::Symbol(symbol) => SyntaxItemKind::SymbolReference(symbol), Declaration::Type(ty) => SyntaxItemKind::TypeReference(ty), }, span, - )); + ); } pub fn is_unresolved(&mut self, ty: TypeId) -> bool { diff --git a/crates/rue-compiler/src/file.rs b/crates/rue-compiler/src/file.rs index a1fe21dd..d1aff91e 100644 --- a/crates/rue-compiler/src/file.rs +++ b/crates/rue-compiler/src/file.rs @@ -20,9 +20,8 @@ use rue_parser::Parser; use thiserror::Error; use crate::{ - Compiler, ImportCache, SyntaxItem, SyntaxItemKind, check_unused, compile_symbol_items, - compile_type_items, declare_module_items, declare_symbol_items, declare_type_items, - resolve_imports, + Compiler, ImportCache, SyntaxItemKind, check_unused, compile_symbol_items, compile_type_items, + declare_module_items, declare_symbol_items, declare_type_items, resolve_imports, }; #[derive(Debug, Error)] @@ -514,13 +513,13 @@ impl File { self.end(ctx, declarations); for scope in ctx.scope_stack().into_iter().rev() { - ctx.syntax_map_mut().add_item(SyntaxItem::new( + ctx.add_syntax( SyntaxItemKind::Scope(scope), TextRange::new( TextSize::from(0), TextSize::from(self.source.text.len() as u32), ), - )); + ); } } } diff --git a/crates/rue-compiler/src/syntax_map.rs b/crates/rue-compiler/src/syntax_map.rs index 7a7ffb33..b1728d0b 100644 --- a/crates/rue-compiler/src/syntax_map.rs +++ b/crates/rue-compiler/src/syntax_map.rs @@ -1,6 +1,7 @@ use std::collections::HashSet; use rowan::TextRange; +use rue_diagnostic::SourceKind; use rue_hir::{ScopeId, SymbolId}; use rue_types::TypeId; @@ -27,11 +28,16 @@ impl SyntaxMap { pub struct SyntaxItem { pub kind: SyntaxItemKind, pub span: TextRange, + pub source_kind: SourceKind, } impl SyntaxItem { - pub fn new(kind: SyntaxItemKind, span: TextRange) -> Self { - Self { kind, span } + pub fn new(kind: SyntaxItemKind, span: TextRange, source_kind: SourceKind) -> Self { + Self { + kind, + span, + source_kind, + } } } diff --git a/crates/rue-lsp/src/cache.rs b/crates/rue-lsp/src/cache.rs index be0aa900..629d8c43 100644 --- a/crates/rue-lsp/src/cache.rs +++ b/crates/rue-lsp/src/cache.rs @@ -3,11 +3,11 @@ use std::{collections::HashSet, sync::Arc}; use indexmap::IndexMap; use rowan::{TextRange, TextSize}; use rue_compiler::{Compiler, CompletionContext, SyntaxItemKind, SyntaxMap}; -use rue_diagnostic::{LineCol, Source}; +use rue_diagnostic::{LineCol, Source, SourceKind}; use rue_hir::{ScopeId, Symbol, SymbolId}; use rue_types::{Type, TypeId, Union}; use tower_lsp::lsp_types::{ - CompletionItem, CompletionItemKind, CompletionItemLabelDetails, Position, Range, + CompletionItem, CompletionItemKind, CompletionItemLabelDetails, Location, Position, Range, Url, }; #[derive(Debug, Clone)] @@ -60,7 +60,7 @@ pub struct Cache { impl Cache> { pub fn new(ctx: Arc, source: Source) -> Self { - let syntax_map = ctx.syntax_map(&source.kind).cloned().unwrap_or_default(); + let syntax_map = ctx.syntax_map().clone(); Self { ctx, @@ -91,6 +91,10 @@ impl Cache { let mut scopes = Vec::new(); for item in self.syntax_map.items() { + if item.source_kind != self.source.kind { + continue; + } + let SyntaxItemKind::Scope(scope) = item.kind else { continue; }; @@ -107,6 +111,7 @@ impl Cache { for item in self .syntax_map .items() + .filter(|item| item.source_kind == self.source.kind) .collect::>() .into_iter() .rev() @@ -313,6 +318,7 @@ impl Cache { for item in self .syntax_map .items() + .filter(|item| item.source_kind == self.source.kind) .map(Clone::clone) .collect::>() { @@ -366,42 +372,56 @@ impl Cache { infos } - pub fn definitions(&self, index: usize) -> Vec { + pub fn definitions(&self, index: usize) -> Vec { self.definitions_impl(index) .into_iter() - .map(|span| { + .filter(|(_, kind)| matches!(kind, SourceKind::File(_))) + .map(|(span, kind)| { + let SourceKind::File(path) = kind else { + unreachable!(); + }; + let start = LineCol::new(&self.source.text, span.start().into()); let end = LineCol::new(&self.source.text, span.end().into()); - Range::new( + let range = Range::new( Position::new(start.line as u32, start.col as u32), Position::new(end.line as u32, end.col as u32), - ) + ); + + Location::new(Url::from_file_path(path).unwrap(), range) }) .collect() } - pub fn references(&self, index: usize) -> Vec { + pub fn references(&self, index: usize) -> Vec { self.references_impl(index) .into_iter() - .map(|span| { + .filter(|(_, kind)| matches!(kind, SourceKind::File(_))) + .map(|(span, kind)| { + let SourceKind::File(path) = kind else { + unreachable!(); + }; + let start = LineCol::new(&self.source.text, span.start().into()); let end = LineCol::new(&self.source.text, span.end().into()); - Range::new( + let range = Range::new( Position::new(start.line as u32, start.col as u32), Position::new(end.line as u32, end.col as u32), - ) + ); + + Location::new(Url::from_file_path(path).unwrap(), range) }) .collect() } - fn definitions_impl(&self, index: usize) -> Vec { - for item in self.syntax_map.items() { - if !contains(item.span, index) { - continue; - } - + fn definitions_impl(&self, index: usize) -> Vec<(TextRange, SourceKind)> { + for item in self + .syntax_map + .items() + .filter(|item| item.source_kind == self.source.kind && contains(item.span, index)) + { match item.kind.clone() { SyntaxItemKind::SymbolDeclaration(symbol) => { return self @@ -413,7 +433,7 @@ impl Cache { }; if declaration == symbol { - Some(item.span) + Some((item.span, item.source_kind.clone())) } else { None } @@ -430,7 +450,7 @@ impl Cache { }; if declaration == symbol { - Some(item.span) + Some((item.span, item.source_kind.clone())) } else { None } @@ -447,7 +467,7 @@ impl Cache { }; if declaration == ty { - Some(item.span) + Some((item.span, item.source_kind.clone())) } else { None } @@ -464,7 +484,7 @@ impl Cache { }; if declaration == ty { - Some(item.span) + Some((item.span, item.source_kind.clone())) } else { None } @@ -483,7 +503,7 @@ impl Cache { if declaration.container == field.container && declaration.name == field.name { - Some(item.span) + Some((item.span, item.source_kind.clone())) } else { None } @@ -502,7 +522,7 @@ impl Cache { if declaration.container == field.container && declaration.name == field.name { - Some(item.span) + Some((item.span, item.source_kind.clone())) } else { None } @@ -518,12 +538,12 @@ impl Cache { Vec::new() } - fn references_impl(&self, index: usize) -> Vec { - for item in self.syntax_map.items() { - if !contains(item.span, index) { - continue; - } - + fn references_impl(&self, index: usize) -> Vec<(TextRange, SourceKind)> { + for item in self + .syntax_map + .items() + .filter(|item| item.source_kind == self.source.kind && contains(item.span, index)) + { match item.kind.clone() { SyntaxItemKind::SymbolDeclaration(symbol) | SyntaxItemKind::SymbolReference(symbol) => { @@ -536,7 +556,7 @@ impl Cache { }; if declaration == symbol { - Some(item.span) + Some((item.span, item.source_kind.clone())) } else { None } @@ -553,7 +573,7 @@ impl Cache { }; if declaration == ty { - Some(item.span) + Some((item.span, item.source_kind.clone())) } else { None } @@ -572,7 +592,7 @@ impl Cache { if declaration.container == field.container && declaration.name == field.name { - Some(item.span) + Some((item.span, item.source_kind.clone())) } else { None } diff --git a/crates/rue-lsp/src/main.rs b/crates/rue-lsp/src/main.rs index b6071892..b3598ad6 100644 --- a/crates/rue-lsp/src/main.rs +++ b/crates/rue-lsp/src/main.rs @@ -244,19 +244,14 @@ impl Backend { let cache = self.cache.lock().unwrap().get(&uri)?.to_cloned(); let position = cache.position(params.text_document_position_params.position); - let range = cache.definitions(position); + let locations = cache.definitions(position); - if range.is_empty() { + if locations.is_empty() { None - } else if range.len() == 1 { - Some(GotoDefinitionResponse::Scalar(Location::new(uri, range[0]))) + } else if locations.len() == 1 { + Some(GotoDefinitionResponse::Scalar(locations[0].clone())) } else { - Some(GotoDefinitionResponse::Array( - range - .into_iter() - .map(|range| Location::new(uri.clone(), range)) - .collect(), - )) + Some(GotoDefinitionResponse::Array(locations)) } } @@ -266,17 +261,12 @@ impl Backend { let cache = self.cache.lock().unwrap().get(&uri)?.to_cloned(); let position = cache.position(params.text_document_position.position); - let range = cache.references(position); + let locations = cache.references(position); - if range.is_empty() { + if locations.is_empty() { None } else { - Some( - range - .into_iter() - .map(|range| Location::new(uri.clone(), range)) - .collect(), - ) + Some(locations) } } From 3723f032a804e9b3c00352409568621067249757 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 17 Nov 2025 10:39:55 +0800 Subject: [PATCH 3/8] goto file --- crates/rue-compiler/src/file.rs | 6 ++++++ crates/rue-compiler/src/syntax_map.rs | 1 + crates/rue-lsp/src/cache.rs | 14 ++++++++++---- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/crates/rue-compiler/src/file.rs b/crates/rue-compiler/src/file.rs index d1aff91e..48c470a9 100644 --- a/crates/rue-compiler/src/file.rs +++ b/crates/rue-compiler/src/file.rs @@ -431,6 +431,12 @@ impl File { declarations: ModuleDeclarations::default(), })); + ctx.add_syntax_for_source( + SyntaxItemKind::FileModule(module), + document.syntax().text_range(), + source.kind.clone(), + ); + Self { name, source, diff --git a/crates/rue-compiler/src/syntax_map.rs b/crates/rue-compiler/src/syntax_map.rs index b1728d0b..f35f679a 100644 --- a/crates/rue-compiler/src/syntax_map.rs +++ b/crates/rue-compiler/src/syntax_map.rs @@ -43,6 +43,7 @@ impl SyntaxItem { #[derive(Debug, Clone)] pub enum SyntaxItemKind { + FileModule(SymbolId), SymbolDeclaration(SymbolId), SymbolReference(SymbolId), TypeDeclaration(TypeId), diff --git a/crates/rue-lsp/src/cache.rs b/crates/rue-lsp/src/cache.rs index 629d8c43..97a96ef3 100644 --- a/crates/rue-lsp/src/cache.rs +++ b/crates/rue-lsp/src/cache.rs @@ -365,7 +365,9 @@ impl Cache { type_name: self.type_name(scopes, field.ty), })); } - SyntaxItemKind::Scope(_) | SyntaxItemKind::CompletionContext(_) => {} + SyntaxItemKind::Scope(_) + | SyntaxItemKind::CompletionContext(_) + | SyntaxItemKind::FileModule(_) => {} } } @@ -445,7 +447,9 @@ impl Cache { .syntax_map .items() .filter_map(|item| { - let SyntaxItemKind::SymbolDeclaration(declaration) = item.kind else { + let (SyntaxItemKind::SymbolDeclaration(declaration) + | SyntaxItemKind::FileModule(declaration)) = item.kind + else { return None; }; @@ -531,7 +535,8 @@ impl Cache { } SyntaxItemKind::FieldInitializer(_) | SyntaxItemKind::Scope(_) - | SyntaxItemKind::CompletionContext(_) => {} + | SyntaxItemKind::CompletionContext(_) + | SyntaxItemKind::FileModule(_) => {} } } @@ -601,7 +606,8 @@ impl Cache { } SyntaxItemKind::FieldInitializer(_) | SyntaxItemKind::Scope(_) - | SyntaxItemKind::CompletionContext(_) => {} + | SyntaxItemKind::CompletionContext(_) + | SyntaxItemKind::FileModule(_) => {} } } From ad63e068be15bcb7b68e8ed2c8bfdf9c9b58c397 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 17 Nov 2025 11:47:13 +0800 Subject: [PATCH 4/8] Path auto completions --- crates/rue-ast/src/lib.rs | 20 ++++- .../src/compile/expr/field_access.rs | 10 ++- crates/rue-compiler/src/compile/expr/path.rs | 2 +- .../src/compile/expr/struct_initializer.rs | 12 ++- crates/rue-compiler/src/compile/path.rs | 64 ++++++++++---- crates/rue-compiler/src/compile/ty/path.rs | 3 +- crates/rue-compiler/src/syntax_map.rs | 3 + crates/rue-lsp/src/cache.rs | 81 ++++++++++++++--- crates/rue-lsp/src/main.rs | 15 +++- crates/rue-parser/src/grammar/expr.rs | 88 ++++++++++++------- crates/rue-parser/src/syntax_kind.rs | 4 + 11 files changed, 225 insertions(+), 77 deletions(-) diff --git a/crates/rue-ast/src/lib.rs b/crates/rue-ast/src/lib.rs index 84968725..e27a9c22 100644 --- a/crates/rue-ast/src/lib.rs +++ b/crates/rue-ast/src/lib.rs @@ -507,10 +507,17 @@ impl AstPathExpr { } impl AstPathSegment { - pub fn initial_separator(&self) -> Option { + pub fn separator(&self) -> Option { self.syntax() - .children() - .find_map(AstLeadingPathSeparator::cast) + .children_with_tokens() + .filter_map(SyntaxElement::into_token) + .find(|token| token.kind() == T![::]) + .filter(|token| { + let Some(name) = self.name() else { + return true; + }; + token.text_range().start() < name.text_range().start() + }) } pub fn name(&self) -> Option { @@ -680,6 +687,13 @@ impl AstFieldAccessExpr { self.syntax().children().find_map(AstExpr::cast) } + pub fn dot(&self) -> Option { + self.syntax() + .children_with_tokens() + .filter_map(SyntaxElement::into_token) + .find(|token| token.kind() == T![.]) + } + pub fn field(&self) -> Option { self.syntax() .children_with_tokens() diff --git a/crates/rue-compiler/src/compile/expr/field_access.rs b/crates/rue-compiler/src/compile/expr/field_access.rs index 6ff8a025..096569c3 100644 --- a/crates/rue-compiler/src/compile/expr/field_access.rs +++ b/crates/rue-compiler/src/compile/expr/field_access.rs @@ -10,22 +10,24 @@ use crate::{ }; pub fn compile_field_access_expr(ctx: &mut Compiler, access: &AstFieldAccessExpr) -> Value { - let mut start = access.syntax().text_range().start(); - let expr = if let Some(expr) = access.expr() { - start = expr.syntax().text_range().end(); compile_expr(ctx, &expr, None) } else { debug!("Unresolved field access expr"); ctx.builtins().unresolved.clone() }; + let Some(dot) = access.dot() else { + debug!("Unresolved field access dot"); + return ctx.builtins().unresolved.clone(); + }; + ctx.add_syntax( SyntaxItemKind::CompletionContext(CompletionContext::StructFields { ty: expr.ty, specified_fields: None, }), - TextRange::new(start, access.syntax().text_range().end()), + TextRange::new(dot.text_range().end(), access.syntax().text_range().end()), ); let Some(name) = access.field() else { diff --git a/crates/rue-compiler/src/compile/expr/path.rs b/crates/rue-compiler/src/compile/expr/path.rs index ff240b49..40e69ee4 100644 --- a/crates/rue-compiler/src/compile/expr/path.rs +++ b/crates/rue-compiler/src/compile/expr/path.rs @@ -6,7 +6,7 @@ use crate::{Compiler, PathKind, PathResult, compile_path}; pub fn compile_path_expr(ctx: &mut Compiler, path: &AstPathExpr) -> Value { let PathResult::Symbol(symbol, override_type, _) = - compile_path(ctx, path.syntax(), path.segments(), PathKind::Symbol) + compile_path(ctx, path.syntax(), path.segments(), PathKind::Symbol, true) else { debug!("Unresolved path expr"); return ctx.builtins().unresolved.clone(); diff --git a/crates/rue-compiler/src/compile/expr/struct_initializer.rs b/crates/rue-compiler/src/compile/expr/struct_initializer.rs index d0c79e6c..050d444c 100644 --- a/crates/rue-compiler/src/compile/expr/struct_initializer.rs +++ b/crates/rue-compiler/src/compile/expr/struct_initializer.rs @@ -17,7 +17,7 @@ pub fn compile_struct_initializer_expr( ) -> Value { let ty = if let Some(path) = expr.path() && let PathResult::Type(ty, _) = - compile_path(ctx, path.syntax(), path.segments(), PathKind::Type) + compile_path(ctx, path.syntax(), path.segments(), PathKind::Type, true) { ty } else { @@ -98,9 +98,13 @@ pub fn compile_struct_initializer_expr( continue; }; - if let PathResult::Symbol(symbol, override_type, _) = - compile_path(ctx, field.syntax(), [name].into_iter(), PathKind::Symbol) - { + if let PathResult::Symbol(symbol, override_type, _) = compile_path( + ctx, + field.syntax(), + [name].into_iter(), + PathKind::Symbol, + false, + ) { let ty = ctx.symbol_type(symbol); let mut value = Value::new(ctx.alloc_hir(Hir::Reference(symbol)), ty) diff --git a/crates/rue-compiler/src/compile/path.rs b/crates/rue-compiler/src/compile/path.rs index fad11ea3..b833aeeb 100644 --- a/crates/rue-compiler/src/compile/path.rs +++ b/crates/rue-compiler/src/compile/path.rs @@ -8,7 +8,7 @@ use rue_hir::{Declaration, ImportId, Symbol, SymbolId}; use rue_parser::SyntaxToken; use rue_types::{Apply, Type, TypeId}; -use crate::{Compiler, GetTextRange, SyntaxItemKind, compile_generic_arguments}; +use crate::{Compiler, CompletionContext, GetTextRange, SyntaxItemKind, compile_generic_arguments}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum PathKind { @@ -24,15 +24,19 @@ pub enum PathResult { } pub trait PathSegment { - fn initial_separator(&self) -> Option; + fn separator(&self) -> Option; + fn text_range(&self) -> TextRange; fn name(&self) -> Option; fn generic_arguments(&self) -> Option; } impl PathSegment for AstPathSegment { - fn initial_separator(&self) -> Option { - self.initial_separator() - .map(|separator| separator.syntax().text_range()) + fn separator(&self) -> Option { + self.separator().map(|separator| separator.text_range()) + } + + fn text_range(&self) -> TextRange { + self.syntax().text_range() } fn name(&self) -> Option { @@ -45,10 +49,14 @@ impl PathSegment for AstPathSegment { } impl PathSegment for SyntaxToken { - fn initial_separator(&self) -> Option { + fn separator(&self) -> Option { None } + fn text_range(&self) -> TextRange { + self.text_range() + } + fn name(&self) -> Option { Some(self.clone()) } @@ -63,6 +71,7 @@ pub fn compile_path( range: &impl GetTextRange, segments: impl Iterator, kind: PathKind, + completions: bool, ) -> PathResult where S: PathSegment, @@ -70,17 +79,35 @@ where let mut segments = segments.collect::>(); let mut base_scope = ctx.last_scope_id(); let mut module_stack = ctx.parent_module_stack().to_vec(); + let mut path_completion_module = None; let length = segments.len(); - while !segments.is_empty() - && let Some(name) = segments[0].name() + if let Some(segment) = segments.first() + && let Some(separator) = segment.separator() + { + ctx.diagnostic(&separator, DiagnosticKind::PathSeparatorInFirstSegment); + } + + while let Some(segment) = segments.first() + && let Some(name) = segment.name() && name.text() == "super" { + if let Some(module) = path_completion_module + && completions + && let Some(separator) = segment.separator() + { + ctx.add_syntax( + SyntaxItemKind::CompletionContext(CompletionContext::ModuleExports { module }), + TextRange::new(separator.end(), segment.text_range().end()), + ); + } + segments.remove(0); if let Some(module) = module_stack.pop() { base_scope = ctx.module(module).scope; + path_completion_module = Some(module); ctx.add_syntax(SyntaxItemKind::SymbolReference(module), name.text_range()); } else { @@ -92,6 +119,16 @@ where let mut previous_name = None; for (index, segment) in segments.iter().enumerate() { + if let Some(module) = path_completion_module + && completions + && let Some(separator) = segment.separator() + { + ctx.add_syntax( + SyntaxItemKind::CompletionContext(CompletionContext::ModuleExports { module }), + TextRange::new(separator.end(), segment.text_range().end()), + ); + } + if let Some(name) = segment.name() && name.text() == "super" { @@ -103,13 +140,6 @@ where return PathResult::Unresolved; } - if index == 0 - && let Some(separator) = segment.initial_separator() - { - ctx.diagnostic(&separator, DiagnosticKind::PathSeparatorInFirstSegment); - return PathResult::Unresolved; - } - let Some(name) = segment.name() else { return PathResult::Unresolved; }; @@ -247,6 +277,10 @@ where } } + if matches!(ctx.symbol(symbol), Symbol::Module(_)) { + path_completion_module = Some(symbol); + } + value = Some(PathResult::Symbol(symbol, override_type, import)); } PathResult::Type(mut ty, import) => { diff --git a/crates/rue-compiler/src/compile/ty/path.rs b/crates/rue-compiler/src/compile/ty/path.rs index 5d061453..093a6d13 100644 --- a/crates/rue-compiler/src/compile/ty/path.rs +++ b/crates/rue-compiler/src/compile/ty/path.rs @@ -5,7 +5,8 @@ use rue_types::TypeId; use crate::{Compiler, PathKind, PathResult, compile_path}; pub fn compile_path_type(ctx: &mut Compiler, path: &AstPathType) -> TypeId { - let PathResult::Type(ty, _) = compile_path(ctx, path.syntax(), path.segments(), PathKind::Type) + let PathResult::Type(ty, _) = + compile_path(ctx, path.syntax(), path.segments(), PathKind::Type, true) else { debug!("Unresolved path type {}", path.syntax().text()); return ctx.builtins().unresolved.ty; diff --git a/crates/rue-compiler/src/syntax_map.rs b/crates/rue-compiler/src/syntax_map.rs index f35f679a..0c13feea 100644 --- a/crates/rue-compiler/src/syntax_map.rs +++ b/crates/rue-compiler/src/syntax_map.rs @@ -73,4 +73,7 @@ pub enum CompletionContext { ty: TypeId, specified_fields: Option>, }, + ModuleExports { + module: SymbolId, + }, } diff --git a/crates/rue-lsp/src/cache.rs b/crates/rue-lsp/src/cache.rs index 97a96ef3..62b5a8b5 100644 --- a/crates/rue-lsp/src/cache.rs +++ b/crates/rue-lsp/src/cache.rs @@ -148,12 +148,18 @@ impl Cache { } pub fn completions(&mut self, scopes: &Scopes, index: usize) -> Vec { + if self.source.text.get(index - 1..index) == Some(":") + && self.source.text.get(index - 2..index) != Some("::") + { + return vec![]; + } + let context = self.completion_context(index); let partial = self.partial_identifier(index).unwrap_or_default(); let mut scored_items = Vec::new(); let mut symbol_names = HashSet::new(); - let mut types_names = HashSet::new(); + let mut type_names = HashSet::new(); if let CompletionContext::StructFields { ty, @@ -226,6 +232,50 @@ impl Cache { } } + if let CompletionContext::ModuleExports { module } = context.clone() { + let scope = self.ctx.module(module).scope; + + for (name, symbol) in self + .ctx + .scope(scope) + .exported_symbols() + .map(|(name, symbol)| (name.to_string(), symbol)) + .collect::>() + { + if !symbol_names.insert(name.to_string()) { + continue; + } + + if let Some(score) = fuzzy_match(&partial, &name) { + let ty = self.ctx.symbol_type_in(scope, symbol); + let type_name = self.type_name(scopes, ty); + let kind = symbol_kind(self.ctx.symbol(symbol)); + scored_items.push((score, create_completion_item(name, kind, Some(type_name)))); + } + } + + for (name, ty) in self + .ctx + .scope(scope) + .exported_types() + .map(|(name, ty)| (name.to_string(), ty)) + .collect::>() + { + if !type_names.insert(name.to_string()) { + continue; + } + + if let Some(score) = fuzzy_match(&partial, &name) { + let type_name = self.inner_type(ty).map(|ty| self.type_name(scopes, ty)); + + scored_items.push(( + score, + create_completion_item(name, Some(CompletionItemKind::STRUCT), type_name), + )); + } + } + } + for scope in &scopes.0 { let symbols = self .ctx @@ -251,17 +301,7 @@ impl Cache { let symbol = self.ctx.scope(*scope).symbol(&name).unwrap(); let ty = self.ctx.symbol_type_in(*scope, symbol); let type_name = self.type_name(scopes, ty); - - let kind = match self.ctx.symbol(symbol) { - Symbol::Binding(_) => Some(CompletionItemKind::VARIABLE), - Symbol::Constant(_) => Some(CompletionItemKind::CONSTANT), - Symbol::Function(_) => Some(CompletionItemKind::FUNCTION), - Symbol::Parameter(_) => Some(CompletionItemKind::VARIABLE), - Symbol::Module(_) => Some(CompletionItemKind::MODULE), - Symbol::Builtin(_) => Some(CompletionItemKind::FUNCTION), - Symbol::Unresolved => None, - }; - + let kind = symbol_kind(self.ctx.symbol(symbol)); scored_items .push((score, create_completion_item(name, kind, Some(type_name)))); } @@ -269,7 +309,7 @@ impl Cache { } for name in types { - if !types_names.insert(name.clone()) { + if !type_names.insert(name.clone()) { continue; } @@ -277,7 +317,8 @@ impl Cache { CompletionContext::Document | CompletionContext::Item | CompletionContext::Statement - | CompletionContext::StructFields { .. } => { + | CompletionContext::StructFields { .. } + | CompletionContext::ModuleExports { .. } => { continue; } CompletionContext::Type => {} @@ -790,3 +831,15 @@ fn create_completion_item( ..Default::default() } } + +fn symbol_kind(symbol: &Symbol) -> Option { + match symbol { + Symbol::Binding(_) => Some(CompletionItemKind::VARIABLE), + Symbol::Constant(_) => Some(CompletionItemKind::CONSTANT), + Symbol::Function(_) => Some(CompletionItemKind::FUNCTION), + Symbol::Parameter(_) => Some(CompletionItemKind::VARIABLE), + Symbol::Module(_) => Some(CompletionItemKind::MODULE), + Symbol::Builtin(_) => Some(CompletionItemKind::FUNCTION), + Symbol::Unresolved => None, + } +} diff --git a/crates/rue-lsp/src/main.rs b/crates/rue-lsp/src/main.rs index b3598ad6..f3657f4a 100644 --- a/crates/rue-lsp/src/main.rs +++ b/crates/rue-lsp/src/main.rs @@ -44,7 +44,11 @@ impl LanguageServer for Backend { definition_provider: Some(OneOf::Left(true)), references_provider: Some(OneOf::Left(true)), completion_provider: Some(CompletionOptions { - trigger_characters: Some(vec![".".to_string(), ",".to_string()]), + trigger_characters: Some(vec![ + ".".to_string(), + ",".to_string(), + ":".to_string(), + ]), all_commit_characters: Some(vec![ ";".to_string(), "(".to_string(), @@ -277,10 +281,13 @@ impl Backend { let position = cache.position(params.text_document_position.position); let scopes = cache.scopes(position); + let completions = cache.completions(&scopes, position); + + if completions.is_empty() { + return None; + } - Some(CompletionResponse::Array( - cache.completions(&scopes, position), - )) + Some(CompletionResponse::Array(completions)) } } diff --git a/crates/rue-parser/src/grammar/expr.rs b/crates/rue-parser/src/grammar/expr.rs index 3e5d6e11..a8be9e02 100644 --- a/crates/rue-parser/src/grammar/expr.rs +++ b/crates/rue-parser/src/grammar/expr.rs @@ -53,12 +53,7 @@ pub fn expr_with(p: &mut Parser, checkpoint: Checkpoint, options: ExprOptions) - ); p.finish(); } else if !options.inline && p.at(T![::]) || p.at(SyntaxKind::Ident) || p.at(T![super]) { - p.start_at(checkpoint, SyntaxKind::PathExpr); - let mut separated = path_expr_segment(p, true, false); - while separated || p.at(T![::]) { - separated = path_expr_segment(p, false, separated); - } - p.finish(); + path_expr(p, checkpoint); if p.at(T!['{']) && options.allow_struct_initializer { p.start_at(checkpoint, SyntaxKind::StructInitializerExpr); @@ -222,31 +217,6 @@ pub fn expr_with(p: &mut Parser, checkpoint: Checkpoint, options: ExprOptions) - } } -fn path_expr_segment(p: &mut Parser, first: bool, separated: bool) -> bool { - p.start(SyntaxKind::PathSegment); - if !separated { - if first { - if p.at(T![::]) { - p.start(SyntaxKind::LeadingPathSeparator); - p.expect(T![::]); - p.finish(); - } - } else { - p.expect(T![::]); - } - } - if !p.try_eat(T![super]) { - p.expect(SyntaxKind::Ident); - } - let mut separated = p.try_eat(T![::]); - if separated && p.at(T![<]) { - generic_arguments(p); - separated = false; - } - p.finish(); - separated -} - fn if_expr( p: &mut Parser, checkpoint: Checkpoint, @@ -287,6 +257,32 @@ fn if_expr( } } +fn path_expr(p: &mut Parser, checkpoint: Checkpoint) { + p.start_at(checkpoint, SyntaxKind::PathExpr); + path_expr_segment(p, true); + while p.at(T![::]) { + path_expr_segment(p, false); + } + p.finish(); +} + +fn path_expr_segment(p: &mut Parser, first: bool) { + p.start(SyntaxKind::PathSegment); + if first { + p.try_eat(T![::]); + } else { + p.expect(T![::]); + } + if !p.try_eat(T![super]) { + p.expect(SyntaxKind::Ident); + } + if p.at(SyntaxKind::TurboFish) { + p.expect(T![::]); + generic_arguments(p); + } + p.finish(); +} + #[cfg(test)] mod tests { use expect_test::expect; @@ -647,4 +643,34 @@ mod tests { expect![""], ); } + + #[test] + fn test_field_access_expr() { + check( + expr, + "hello.world", + expect![[r#" + FieldAccessExpr@0..11 + PathExpr@0..5 + PathSegment@0..5 + Ident@0..5 "hello" + Dot@5..6 "." + Ident@6..11 "world" + "#]], + expect![""], + ); + + check( + expr, + "hello.", + expect![[r#" + FieldAccessExpr@0..6 + PathExpr@0..5 + PathSegment@0..5 + Ident@0..5 "hello" + Dot@5..6 "." + "#]], + expect!["Expected identifier, found eof at main.rue:1:7"], + ); + } } diff --git a/crates/rue-parser/src/syntax_kind.rs b/crates/rue-parser/src/syntax_kind.rs index 7279e99c..bb848e92 100644 --- a/crates/rue-parser/src/syntax_kind.rs +++ b/crates/rue-parser/src/syntax_kind.rs @@ -212,6 +212,9 @@ pub enum SyntaxKind { #[display("`::`")] PathSeparator, + #[display("`::<`")] + TurboFish, + #[display("`...`")] Spread, @@ -558,6 +561,7 @@ impl SyntaxKind { T![=>] => &[T![=], T![>]], T![::] => &[T![:], T![:]], T![...] => &[T![.], T![.], T![.]], + SyntaxKind::TurboFish => &[T![:], T![:], T![<]], SyntaxKind::Document => &[SyntaxKind::Document], SyntaxKind::ModuleItem => &[SyntaxKind::ModuleItem], SyntaxKind::FunctionItem => &[SyntaxKind::FunctionItem], From 3f90784f92e052b5b03d57b5f5bca7a13de9b1bb Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 17 Nov 2025 11:54:15 +0800 Subject: [PATCH 5/8] Subcontext awareness --- crates/rue-lsp/src/cache.rs | 61 +++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/crates/rue-lsp/src/cache.rs b/crates/rue-lsp/src/cache.rs index 62b5a8b5..d74044ed 100644 --- a/crates/rue-lsp/src/cache.rs +++ b/crates/rue-lsp/src/cache.rs @@ -107,7 +107,11 @@ impl Cache { Scopes(scopes) } - fn completion_context(&self, index: usize) -> CompletionContext { + fn completion_context(&self, index: usize) -> (CompletionContext, CompletionContext) { + let mut first = CompletionContext::Document; + let mut second = CompletionContext::Document; + let mut has_first = false; + for item in self .syntax_map .items() @@ -121,11 +125,17 @@ impl Cache { }; if contains(item.span, index) { - return context.clone(); + if has_first { + second = context.clone(); + break; + } + + first = context.clone(); + has_first = true; } } - CompletionContext::Document + (first, second) } fn partial_identifier(&self, index: usize) -> Option { @@ -154,7 +164,7 @@ impl Cache { return vec![]; } - let context = self.completion_context(index); + let (context, subcontext) = self.completion_context(index); let partial = self.partial_identifier(index).unwrap_or_default(); let mut scored_items = Vec::new(); @@ -246,7 +256,11 @@ impl Cache { continue; } - if let Some(score) = fuzzy_match(&partial, &name) { + if let Some(score) = fuzzy_match(&partial, &name) + && (matches!(subcontext, CompletionContext::Expression) + || (matches!(self.ctx.symbol(symbol), Symbol::Module(_)) + && matches!(subcontext, CompletionContext::Type))) + { let ty = self.ctx.symbol_type_in(scope, symbol); let type_name = self.type_name(scopes, ty); let kind = symbol_kind(self.ctx.symbol(symbol)); @@ -265,6 +279,25 @@ impl Cache { continue; } + match subcontext { + CompletionContext::Document + | CompletionContext::Item + | CompletionContext::Statement + | CompletionContext::StructFields { .. } + | CompletionContext::ModuleExports { .. } => { + continue; + } + CompletionContext::Type => {} + CompletionContext::Expression => { + let semantic = rue_types::unwrap_semantic(self.ctx.types_mut(), ty, true); + + match self.ctx.ty(semantic) { + Type::Struct(_) => {} + _ => continue, + } + } + } + if let Some(score) = fuzzy_match(&partial, &name) { let type_name = self.inner_type(ty).map(|ty| self.type_name(scopes, ty)); @@ -291,14 +324,18 @@ impl Cache { .map(ToString::to_string) .collect::>(); - if matches!(context, CompletionContext::Expression) { - for name in symbols { - if !symbol_names.insert(name.clone()) { - continue; - } + for name in symbols { + if !symbol_names.insert(name.clone()) { + continue; + } + + if let Some(score) = fuzzy_match(&partial, &name) { + let symbol = self.ctx.scope(*scope).symbol(&name).unwrap(); - if let Some(score) = fuzzy_match(&partial, &name) { - let symbol = self.ctx.scope(*scope).symbol(&name).unwrap(); + if matches!(context, CompletionContext::Expression) + || (matches!(self.ctx.symbol(symbol), Symbol::Module(_)) + && matches!(context, CompletionContext::Type)) + { let ty = self.ctx.symbol_type_in(*scope, symbol); let type_name = self.type_name(scopes, ty); let kind = symbol_kind(self.ctx.symbol(symbol)); From d149842c06499455fb9c0dc422f3f67c42aca575 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 17 Nov 2025 12:04:26 +0800 Subject: [PATCH 6/8] LSP fixes --- crates/rue-compiler/src/compile/path.rs | 13 ++++++-- crates/rue-compiler/src/syntax_map.rs | 1 + crates/rue-lsp/src/cache.rs | 41 +++++++++++++++++++++++-- 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/crates/rue-compiler/src/compile/path.rs b/crates/rue-compiler/src/compile/path.rs index b833aeeb..a458be1b 100644 --- a/crates/rue-compiler/src/compile/path.rs +++ b/crates/rue-compiler/src/compile/path.rs @@ -71,7 +71,7 @@ pub fn compile_path( range: &impl GetTextRange, segments: impl Iterator, kind: PathKind, - completions: bool, + mut completions: bool, ) -> PathResult where S: PathSegment, @@ -98,7 +98,10 @@ where && let Some(separator) = segment.separator() { ctx.add_syntax( - SyntaxItemKind::CompletionContext(CompletionContext::ModuleExports { module }), + SyntaxItemKind::CompletionContext(CompletionContext::ModuleExports { + module, + allow_super: true, + }), TextRange::new(separator.end(), segment.text_range().end()), ); } @@ -112,6 +115,7 @@ where ctx.add_syntax(SyntaxItemKind::SymbolReference(module), name.text_range()); } else { ctx.diagnostic(&name, DiagnosticKind::UnresolvedSuper); + completions = false; } } @@ -124,7 +128,10 @@ where && let Some(separator) = segment.separator() { ctx.add_syntax( - SyntaxItemKind::CompletionContext(CompletionContext::ModuleExports { module }), + SyntaxItemKind::CompletionContext(CompletionContext::ModuleExports { + module, + allow_super: index == 0, + }), TextRange::new(separator.end(), segment.text_range().end()), ); } diff --git a/crates/rue-compiler/src/syntax_map.rs b/crates/rue-compiler/src/syntax_map.rs index 0c13feea..09ee8352 100644 --- a/crates/rue-compiler/src/syntax_map.rs +++ b/crates/rue-compiler/src/syntax_map.rs @@ -75,5 +75,6 @@ pub enum CompletionContext { }, ModuleExports { module: SymbolId, + allow_super: bool, }, } diff --git a/crates/rue-lsp/src/cache.rs b/crates/rue-lsp/src/cache.rs index d74044ed..327eca57 100644 --- a/crates/rue-lsp/src/cache.rs +++ b/crates/rue-lsp/src/cache.rs @@ -158,13 +158,21 @@ impl Cache { } pub fn completions(&mut self, scopes: &Scopes, index: usize) -> Vec { + let (context, subcontext) = self.completion_context(index); + if self.source.text.get(index - 1..index) == Some(":") - && self.source.text.get(index - 2..index) != Some("::") + && (self.source.text.get(index - 2..index) != Some("::") + || !matches!(context, CompletionContext::ModuleExports { .. })) + { + return vec![]; + } + + if self.source.text.get(index - 1..index) == Some(".") + && !matches!(context, CompletionContext::StructFields { .. }) { return vec![]; } - let (context, subcontext) = self.completion_context(index); let partial = self.partial_identifier(index).unwrap_or_default(); let mut scored_items = Vec::new(); @@ -242,7 +250,22 @@ impl Cache { } } - if let CompletionContext::ModuleExports { module } = context.clone() { + if let CompletionContext::ModuleExports { + module, + allow_super, + } = context.clone() + { + if allow_super && let Some(score) = fuzzy_match(&partial, "super") { + scored_items.push(( + score, + create_completion_item( + "super".to_string(), + Some(CompletionItemKind::MODULE), + None, + ), + )); + } + let scope = self.ctx.module(module).scope; for (name, symbol) in self @@ -309,6 +332,18 @@ impl Cache { } } + if let Some(score) = fuzzy_match(&partial, "super") + && matches!( + context, + CompletionContext::Expression | CompletionContext::Type + ) + { + scored_items.push(( + score, + create_completion_item("super".to_string(), Some(CompletionItemKind::MODULE), None), + )); + } + for scope in &scopes.0 { let symbols = self .ctx From 23656abb1a46de99560e8b6ad58aa0c984f0d9b5 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 17 Nov 2025 13:31:53 +0800 Subject: [PATCH 7/8] Leading :: --- crates/rue-ast/src/lib.rs | 8 ++++---- crates/rue-compiler/src/compile/imports.rs | 6 ++++++ crates/rue-parser/src/syntax_kind.rs | 4 ---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/crates/rue-ast/src/lib.rs b/crates/rue-ast/src/lib.rs index e27a9c22..904ae660 100644 --- a/crates/rue-ast/src/lib.rs +++ b/crates/rue-ast/src/lib.rs @@ -86,7 +86,6 @@ ast_nodes!( DebugStmt, PathExpr, PathSegment, - LeadingPathSeparator, StructInitializerExpr, StructInitializerField, LiteralExpr, @@ -365,10 +364,11 @@ impl AstImportPath { } impl AstImportPathSegment { - pub fn initial_separator(&self) -> Option { + pub fn separator(&self) -> Option { self.syntax() - .children() - .find_map(AstLeadingPathSeparator::cast) + .children_with_tokens() + .filter_map(SyntaxElement::into_token) + .find(|token| token.kind() == T![::]) } pub fn name(&self) -> Option { diff --git a/crates/rue-compiler/src/compile/imports.rs b/crates/rue-compiler/src/compile/imports.rs index 501ddadf..9a4130a8 100644 --- a/crates/rue-compiler/src/compile/imports.rs +++ b/crates/rue-compiler/src/compile/imports.rs @@ -41,6 +41,12 @@ fn construct_imports( ) -> Vec { let mut has_non_super = false; + if let Some(segment) = segments.first() + && let Some(separator) = segment.separator() + { + ctx.diagnostic(&separator, DiagnosticKind::PathSeparatorInFirstSegment); + } + for segment in segments.iter().take(segments.len() - 1) { if let Some(name) = segment.name() { if name.text() == "super" { diff --git a/crates/rue-parser/src/syntax_kind.rs b/crates/rue-parser/src/syntax_kind.rs index bb848e92..fa64b633 100644 --- a/crates/rue-parser/src/syntax_kind.rs +++ b/crates/rue-parser/src/syntax_kind.rs @@ -315,9 +315,6 @@ pub enum SyntaxKind { #[display("path segment")] PathSegment, - #[display("leading path separator")] - LeadingPathSeparator, - #[display("struct initializer expression")] StructInitializerExpr, @@ -594,7 +591,6 @@ impl SyntaxKind { SyntaxKind::DebugStmt => &[SyntaxKind::DebugStmt], SyntaxKind::PathExpr => &[SyntaxKind::PathExpr], SyntaxKind::PathSegment => &[SyntaxKind::PathSegment], - SyntaxKind::LeadingPathSeparator => &[SyntaxKind::LeadingPathSeparator], SyntaxKind::StructInitializerExpr => &[SyntaxKind::StructInitializerExpr], SyntaxKind::StructInitializerField => &[SyntaxKind::StructInitializerField], SyntaxKind::LiteralExpr => &[SyntaxKind::LiteralExpr], From 15aeabe0d868b92d2b2f4094118b9c8bc1070098 Mon Sep 17 00:00:00 2001 From: Rigidity Date: Mon, 17 Nov 2025 13:38:18 +0800 Subject: [PATCH 8/8] Fix pathing --- crates/rue-compiler/src/compile/imports.rs | 11 ++++++----- crates/rue-compiler/src/compile/path.rs | 20 ++++++++++++-------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/crates/rue-compiler/src/compile/imports.rs b/crates/rue-compiler/src/compile/imports.rs index 9a4130a8..bab3068b 100644 --- a/crates/rue-compiler/src/compile/imports.rs +++ b/crates/rue-compiler/src/compile/imports.rs @@ -254,12 +254,13 @@ fn resolve_import( base.symbol(name.text()) .filter(|s| base.is_symbol_exported(*s)) .map(|s| (s, base.symbol_import(s))) - } else { + } else if import.base_scope == import_scope { ctx.resolve_symbol_in(import.base_scope, name.text()) - .filter(|s| { - import.base_scope == import_scope - || ctx.scope(import.base_scope).is_symbol_exported(s.0) - }) + } else { + let base = ctx.scope(import.base_scope); + base.symbol(name.text()) + .filter(|s| base.is_symbol_exported(*s)) + .map(|s| (s, base.symbol_import(s))) }; let Some((symbol, import)) = symbol else { diff --git a/crates/rue-compiler/src/compile/path.rs b/crates/rue-compiler/src/compile/path.rs index a458be1b..84735595 100644 --- a/crates/rue-compiler/src/compile/path.rs +++ b/crates/rue-compiler/src/compile/path.rs @@ -174,21 +174,25 @@ where ); return PathResult::Unresolved; } - } else { + } else if base_scope == ctx.last_scope_id() { let symbol = ctx .resolve_symbol_in(base_scope, name.text()) - .filter(|s| { - base_scope == ctx.last_scope_id() - || ctx.scope(base_scope).is_symbol_exported(s.0) - }) .map(|(symbol, import)| (symbol, true, import)); let ty = ctx .resolve_type_in(base_scope, name.text()) - .filter(|t| { - base_scope == ctx.last_scope_id() || ctx.scope(base_scope).is_type_exported(t.0) - }) .map(|(ty, import)| (ty, true, import)); (symbol, ty) + } else { + let base = ctx.scope(base_scope); + let symbol = base + .symbol(name.text()) + .filter(|s| base.is_symbol_exported(*s)) + .map(|s| (s, true, base.symbol_import(s))); + let ty = base + .ty(name.text()) + .filter(|t| base.is_type_exported(*t)) + .map(|t| (t, true, base.type_import(t))); + (symbol, ty) }; let initial = match (symbol, ty) {