Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions crates/squawk_ide/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ use crate::builtins::builtins_file;
use crate::column_name::ColumnName;
use crate::db::{File, bind, parse};
use crate::infer::{Type, infer_type_from_expr, infer_type_from_ty};
use crate::name::{self, Name};
use crate::resolve::{
ResolvedTableName, extract_table_schema_from_path, find_from_item_in_from_clause,
qualified_star_table_name, resolve_cte_table, resolve_table_name, table_ptr_from_from_item,
ResolvedTableName, find_from_item_in_from_clause, qualified_star_table_name, resolve_cte_table,
resolve_table_name, table_ptr_from_from_item,
};
use crate::symbols::{Name, SymbolKind};
use crate::symbols::SymbolKind;
use salsa::Database as Db;
use squawk_syntax::{
SyntaxNodePtr,
Expand Down Expand Up @@ -38,7 +39,7 @@ fn columns_from_create_table_impl(

if let Some(inherits) = create_table.inherits() {
for path in inherits.paths() {
if let Some((table_name, schema)) = extract_table_schema_from_path(&path) {
if let Some((schema, table_name)) = name::schema_and_name_path(&path) {
let position = path.syntax().text_range().start();
if let Some(ResolvedTableName::Table(parent_table)) =
resolve_table_name(db, file, &table_name, &schema, position)
Expand All @@ -60,7 +61,7 @@ fn columns_from_create_table_impl(
}
ast::TableArg::LikeClause(like_clause) => {
if let Some(path) = like_clause.path()
&& let Some((table_name, schema)) = extract_table_schema_from_path(&path)
&& let Some((schema, table_name)) = name::schema_and_name_path(&path)
{
let position = path.syntax().text_range().start();
if let Some(ResolvedTableName::Table(source_table)) =
Expand Down Expand Up @@ -106,7 +107,7 @@ fn table_columns_impl(

if let Some(inherits) = create_table.inherits() {
for path in inherits.paths() {
if let Some((table_name, schema)) = extract_table_schema_from_path(&path) {
if let Some((schema, table_name)) = name::schema_and_name_path(&path) {
let position = path.syntax().text_range().start();
if let Some(ResolvedTableName::Table(parent_table)) =
resolve_table_name(db, file, &table_name, &schema, position)
Expand All @@ -129,7 +130,7 @@ fn table_columns_impl(
}
ast::TableArg::LikeClause(like_clause) => {
if let Some(path) = like_clause.path()
&& let Some((table_name, schema)) = extract_table_schema_from_path(&path)
&& let Some((schema, table_name)) = name::schema_and_name_path(&path)
{
let position = path.syntax().text_range().start();
if let Some(ResolvedTableName::Table(source_table)) =
Expand Down Expand Up @@ -513,7 +514,7 @@ fn select_variant_columns_with_types(
let Some(path) = table.relation_name().and_then(|r| r.path()) else {
return vec![];
};
let Some((table_name, schema)) = extract_table_schema_from_path(&path) else {
let Some((schema, table_name)) = name::schema_and_name_path(&path) else {
return vec![];
};

Expand Down
7 changes: 4 additions & 3 deletions crates/squawk_ide/src/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ use crate::ast_nav;
use crate::binder;
use crate::collect;
use crate::db::{File, bind, parse};
use crate::name::{self, Name, Schema};
use crate::resolve;
use crate::symbols::{Name, Schema, SymbolKind};
use crate::symbols::SymbolKind;
use crate::tokens::is_string_or_comment;

const COMPLETION_MARKER: &str = "squawkCompletionMarker";
Expand Down Expand Up @@ -625,7 +626,7 @@ fn delete_expr_completions(
return completions;
};

let Some(delete_table_name) = resolve::extract_table_name(&path) else {
let Some(delete_table_name) = name::table_name(&path) else {
return completions;
};

Expand Down Expand Up @@ -667,7 +668,7 @@ fn delete_expr_completions(
});
}

let schema = resolve::extract_schema_name(&path);
let schema = name::schema_name(&path);
if let Some(table_ptr) =
binder.lookup_with(&delete_table_name, SymbolKind::Table, position, &schema)
&& let Some(create_table) = table_ptr
Expand Down
2 changes: 2 additions & 0 deletions crates/squawk_ide/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ use crate::db::File;
/// * `InFile<SyntaxNode>` -- syntax node in a file
/// * `InFile<ast::FnDef>` -- ast node in a file
/// * `InFile<TextSize>` -- offset in a file
#[allow(dead_code)]
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub struct InFileWrapper<FileKind, T> {
pub file_id: FileKind,
pub value: T,
}
#[expect(unused)]
pub type InFile<T> = InFileWrapper<File, T>;
2 changes: 2 additions & 0 deletions crates/squawk_ide/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod completion;
pub mod db;
pub mod document_symbols;
pub mod expand_selection;
mod file;
pub mod find_references;
pub mod folding_ranges;
mod generated;
Expand All @@ -18,6 +19,7 @@ pub mod hover;
mod infer;
pub mod inlay_hints;
pub mod location;
mod name;
mod offsets;
mod quote;
mod resolve;
Expand Down
168 changes: 168 additions & 0 deletions crates/squawk_ide/src/name.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
use smol_str::SmolStr;
use squawk_syntax::ast::{self, AstNode};
use std::fmt;

use crate::quote::normalize_identifier;

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) struct Name(pub(crate) SmolStr);

#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct Schema(pub(crate) Name);

impl Schema {
pub(crate) fn new(name: impl Into<SmolStr>) -> Self {
Schema(Name::from_string(name))
}
}

impl fmt::Display for Schema {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0.0)
}
}

impl Name {
pub(crate) fn from_string(text: impl Into<SmolStr>) -> Self {
let text = text.into();
let normalized = normalize_identifier(&text);
Name(normalized.into())
}
pub(crate) fn from_node(node: &impl ast::NameLike) -> Self {
let text = node.syntax().text().to_string();
let normalized = normalize_identifier(&text);
Name(normalized.into())
}
}

impl fmt::Display for Name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}

pub(crate) fn schema_and_name_path(path: &ast::Path) -> Option<(Option<Schema>, Name)> {
Some((schema_name(path), table_name(path)?))
}

pub(crate) fn schema_and_table_name(name_ref: &ast::NameRef) -> Option<(Option<Schema>, Name)> {
if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
return schema_and_name_path(&path);
}

Some((None, Name::from_node(name_ref)))
}

pub(crate) fn schema_and_name(name_ref: &ast::NameRef) -> (Option<Schema>, Name) {
let table_name = Name::from_node(name_ref);
let schema = if let Some(parent) = name_ref.syntax().parent()
&& let Some(field_expr) = ast::FieldExpr::cast(parent)
&& let Some(base) = field_expr.base()
&& let Some(schema_name_ref) = ast::NameRef::cast(base.syntax().clone())
{
Some(Schema(Name::from_node(&schema_name_ref)))
} else {
None
};

(schema, table_name)
}

pub(crate) fn schema_and_func_name(call_expr: &ast::CallExpr) -> Option<(Option<Schema>, Name)> {
match call_expr.expr()? {
ast::Expr::NameRef(name_ref) => Some((None, Name::from_node(&name_ref))),
ast::Expr::FieldExpr(field_expr) => {
let function_name = Name::from_node(&field_expr.field()?);
let ast::Expr::NameRef(schema_name_ref) = field_expr.base()? else {
return None;
};
let schema = Schema(Name::from_node(&schema_name_ref));
Some((Some(schema), function_name))
}
_ => None,
}
}

pub(crate) fn table_name(path: &ast::Path) -> Option<Name> {
let segment = path.segment()?;
let name_ref = segment.name_ref()?;
Some(Name::from_node(&name_ref))
}

pub(crate) fn schema_name(path: &ast::Path) -> Option<Schema> {
path.qualifier()
.and_then(|q| q.segment())
.and_then(|s| s.name_ref())
.map(|name_ref| Schema(Name::from_node(&name_ref)))
}

pub(crate) fn schema_and_type_name(ty: &ast::Type) -> Option<(Option<Schema>, Name)> {
match ty {
ast::Type::ArrayType(array_type) => {
let inner = array_type.ty()?;
schema_and_type_name(&inner)
}
ast::Type::BitType(bit_type) => {
let name = if bit_type.varying_token().is_some() {
"varbit"
} else {
"bit"
};
Some((None, Name::from_string(name)))
}
ast::Type::IntervalType(_) => Some((None, Name::from_string("interval"))),
ast::Type::PathType(path_type) => {
let path = path_type.path()?;
schema_and_name_path(&path)
}
ast::Type::ExprType(expr_type) => {
let expr = expr_type.expr()?;
if let ast::Expr::FieldExpr(field_expr) = expr
&& let Some(field) = field_expr.field()
&& let Some(ast::Expr::NameRef(schema_name_ref)) = field_expr.base()
{
let type_name = Name::from_node(&field);
let schema = Some(Schema(Name::from_node(&schema_name_ref)));
Some((schema, type_name))
} else {
None
}
}
ast::Type::CharType(char_type) => {
let name = if char_type.varchar_token().is_some() || char_type.varying_token().is_some()
{
"varchar"
} else {
"bpchar"
};
Some((None, Name::from_string(name)))
}
ast::Type::DoubleType(_) => Some((None, Name::from_string("float8"))),
ast::Type::TimeType(time_type) => {
let mut name = if time_type.timestamp_token().is_some() {
"timestamp".to_string()
} else {
"time".to_string()
};
if let Some(ast::Timezone::WithTimezone(_)) = time_type.timezone() {
name.push_str("tz");
}
Some((None, Name::from_string(name)))
}
ast::Type::PercentType(_) => None,
}
}

#[cfg(test)]
mod test {
use super::*;
#[test]
fn name_case_insensitive_compare() {
assert_eq!(Name::from_string("foo"), Name::from_string("FOO"));
}

#[test]
fn name_quote_comparing() {
assert_eq!(Name::from_string(r#""foo""#), Name::from_string("foo"));
}
}
Loading
Loading