Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 3dda257

Browse files
feat(hover): hover on functions + add hover integration tests (#485)
1 parent d3d040a commit 3dda257

21 files changed

+827
-26
lines changed

‎Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎crates/pgt_completions/src/relevance/filtering.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ impl CompletionFilter<'_> {
9090
.is_none_or(|n| n != &WrappingNode::List)
9191
&& (ctx.before_cursor_matches_kind(&["keyword_into"])
9292
|| (ctx.before_cursor_matches_kind(&["."])
93-
&& ctx.parent_matches_one_of_kind(&["object_reference"])))
93+
&& ctx.matches_ancestor_history(&["object_reference"])))
9494
}
9595

9696
WrappingClause::DropTable | WrappingClause::AlterTable => ctx
@@ -136,7 +136,7 @@ impl CompletionFilter<'_> {
136136
WrappingClause::Where => {
137137
ctx.before_cursor_matches_kind(&["keyword_and", "keyword_where"])
138138
|| (ctx.before_cursor_matches_kind(&["."])
139-
&& ctx.parent_matches_one_of_kind(&["field"]))
139+
&& ctx.matches_ancestor_history(&["field"]))
140140
}
141141

142142
WrappingClause::PolicyCheck => {

‎crates/pgt_hover/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ tree-sitter.workspace = true
2727
tree_sitter_sql.workspace = true
2828

2929
[dev-dependencies]
30+
insta = { version = "1.42.1" }
3031
pgt_test_utils.workspace = true
3132

3233
[lib]

‎crates/pgt_hover/src/contextual_priority.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use pgt_schema_cache::{Column, Table};
1+
use pgt_schema_cache::{Column, Function,Table};
22
use pgt_treesitter::context::TreesitterContext;
33

44
pub(crate) trait ContextualPriority {
@@ -74,6 +74,34 @@ impl ContextualPriority for Table {
7474
}
7575
}
7676

77+
impl ContextualPriority for Function {
78+
fn relevance_score(&self, _ctx: &TreesitterContext) -> f32 {
79+
let mut score = 0.0;
80+
81+
// built-in functions get higher priority
82+
if self.language == "internal" {
83+
score += 100.0;
84+
}
85+
86+
// public schema functions get base priority
87+
if self.schema == "public" {
88+
score += 50.0;
89+
} else {
90+
score += 25.0;
91+
}
92+
93+
// aggregate and window functions are commonly used
94+
match self.kind {
95+
pgt_schema_cache::ProcKind::Aggregate => score += 20.0,
96+
pgt_schema_cache::ProcKind::Window => score += 15.0,
97+
pgt_schema_cache::ProcKind::Function => score += 10.0,
98+
pgt_schema_cache::ProcKind::Procedure => score += 5.0,
99+
}
100+
101+
score
102+
}
103+
}
104+
77105
/// Will first sort the items by a score and then filter out items with a score gap algorithm.
78106
///
79107
/// `[200, 180, 150, 140]` => all items are returned

‎crates/pgt_hover/src/hovered_item.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::{contextual_priority::ContextualPriority, to_markdown::ToHoverMarkdow
55
pub(crate) enum HoverItem<'a> {
66
Table(&'a pgt_schema_cache::Table),
77
Column(&'a pgt_schema_cache::Column),
8+
Function(&'a pgt_schema_cache::Function),
89
}
910

1011
impl<'a> From<&'a pgt_schema_cache::Table> for HoverItem<'a> {
@@ -19,11 +20,18 @@ impl<'a> From<&'a pgt_schema_cache::Column> for HoverItem<'a> {
1920
}
2021
}
2122

23+
impl<'a> From<&'a pgt_schema_cache::Function> for HoverItem<'a> {
24+
fn from(value: &'a pgt_schema_cache::Function) -> Self {
25+
HoverItem::Function(value)
26+
}
27+
}
28+
2229
impl ContextualPriority for HoverItem<'_> {
2330
fn relevance_score(&self, ctx: &pgt_treesitter::TreesitterContext) -> f32 {
2431
match self {
2532
HoverItem::Table(table) => table.relevance_score(ctx),
2633
HoverItem::Column(column) => column.relevance_score(ctx),
34+
HoverItem::Function(function) => function.relevance_score(ctx),
2735
}
2836
}
2937
}
@@ -33,20 +41,23 @@ impl ToHoverMarkdown for HoverItem<'_> {
3341
match self {
3442
HoverItem::Table(table) => ToHoverMarkdown::hover_headline(*table, writer),
3543
HoverItem::Column(column) => ToHoverMarkdown::hover_headline(*column, writer),
44+
HoverItem::Function(function) => ToHoverMarkdown::hover_headline(*function, writer),
3645
}
3746
}
3847

3948
fn hover_body<W: std::fmt::Write>(&self, writer: &mut W) -> Result<bool, std::fmt::Error> {
4049
match self {
4150
HoverItem::Table(table) => ToHoverMarkdown::hover_body(*table, writer),
4251
HoverItem::Column(column) => ToHoverMarkdown::hover_body(*column, writer),
52+
HoverItem::Function(function) => ToHoverMarkdown::hover_body(*function, writer),
4353
}
4454
}
4555

4656
fn hover_footer<W: std::fmt::Write>(&self, writer: &mut W) -> Result<bool, std::fmt::Error> {
4757
match self {
4858
HoverItem::Table(table) => ToHoverMarkdown::hover_footer(*table, writer),
4959
HoverItem::Column(column) => ToHoverMarkdown::hover_footer(*column, writer),
60+
HoverItem::Function(function) => ToHoverMarkdown::hover_footer(*function, writer),
5061
}
5162
}
5263
}

‎crates/pgt_hover/src/hovered_node.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ impl HoveredNode {
2525
let under_node = ctx.node_under_cursor.as_ref()?;
2626

2727
match under_node.kind() {
28-
"identifier" if ctx.parent_matches_one_of_kind(&["object_reference", "relation"]) => {
28+
"identifier" if ctx.matches_ancestor_history(&["relation", "object_reference"]) => {
2929
if let Some(schema) = ctx.schema_or_alias_name.as_ref() {
3030
Some(HoveredNode::Table(NodeIdentification::SchemaAndName((
3131
schema.clone(),
@@ -35,7 +35,7 @@ impl HoveredNode {
3535
Some(HoveredNode::Table(NodeIdentification::Name(node_content)))
3636
}
3737
}
38-
"identifier" if ctx.parent_matches_one_of_kind(&["field"]) => {
38+
"identifier" if ctx.matches_ancestor_history(&["field"]) => {
3939
if let Some(table_or_alias) = ctx.schema_or_alias_name.as_ref() {
4040
Some(HoveredNode::Column(NodeIdentification::SchemaAndName((
4141
table_or_alias.clone(),
@@ -45,6 +45,18 @@ impl HoveredNode {
4545
Some(HoveredNode::Column(NodeIdentification::Name(node_content)))
4646
}
4747
}
48+
"identifier" if ctx.matches_ancestor_history(&["invocation", "object_reference"]) => {
49+
if let Some(schema) = ctx.schema_or_alias_name.as_ref() {
50+
Some(HoveredNode::Function(NodeIdentification::SchemaAndName((
51+
schema.clone(),
52+
node_content,
53+
))))
54+
} else {
55+
Some(HoveredNode::Function(NodeIdentification::Name(
56+
node_content,
57+
)))
58+
}
59+
}
4860
_ => None,
4961
}
5062
}

‎crates/pgt_hover/src/lib.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,16 @@ pub fn on_hover(params: OnHoverParams) -> Vec<String> {
5656
.collect(),
5757

5858
hovered_node::NodeIdentification::SchemaAndName((table_or_alias, column_name)) => {
59+
// resolve alias to actual table name if needed
60+
let actual_table = ctx
61+
.mentioned_table_aliases
62+
.get(table_or_alias.as_str())
63+
.map(|s| s.as_str())
64+
.unwrap_or(table_or_alias.as_str());
65+
5966
params
6067
.schema_cache
61-
.find_cols(&column_name, Some(&table_or_alias), None)
68+
.find_cols(&column_name, Some(actual_table), None)
6269
.into_iter()
6370
.map(HoverItem::from)
6471
.collect()
@@ -67,6 +74,24 @@ pub fn on_hover(params: OnHoverParams) -> Vec<String> {
6774
hovered_node::NodeIdentification::SchemaAndTableAndName(_) => vec![],
6875
},
6976

77+
HoveredNode::Function(node_identification) => match node_identification {
78+
hovered_node::NodeIdentification::Name(function_name) => params
79+
.schema_cache
80+
.find_functions(&function_name, None)
81+
.into_iter()
82+
.map(HoverItem::from)
83+
.collect(),
84+
85+
hovered_node::NodeIdentification::SchemaAndName((schema, function_name)) => params
86+
.schema_cache
87+
.find_functions(&function_name, Some(&schema))
88+
.into_iter()
89+
.map(HoverItem::from)
90+
.collect(),
91+
92+
hovered_node::NodeIdentification::SchemaAndTableAndName(_) => vec![],
93+
},
94+
7095
_ => todo!(),
7196
};
7297

‎crates/pgt_hover/src/to_markdown.rs

Lines changed: 107 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,35 +17,39 @@ pub(crate) fn format_hover_markdown<T: ToHoverMarkdown>(
1717
item.hover_headline(&mut markdown)?;
1818
markdown_newline(&mut markdown)?;
1919

20-
if item.hover_body(&mutmarkdown)? {
21-
markdown_newline(&mut markdown)?;
22-
}
20+
write!(markdown,"#### ")?;
21+
item.hover_body(&mut markdown)?;
22+
markdown_newline(&mut markdown)?;
2323

24+
write!(markdown, "--- ")?;
25+
markdown_newline(&mut markdown)?;
2426
item.hover_footer(&mut markdown)?;
2527

2628
Ok(markdown)
2729
}
2830

2931
impl ToHoverMarkdown for pgt_schema_cache::Table {
3032
fn hover_headline<W: Write>(&self, writer: &mut W) -> Result<(), std::fmt::Error> {
33+
write!(writer, "`{}.{}`", self.schema, self.name)?;
34+
3135
let table_kind = match self.table_kind {
3236
pgt_schema_cache::TableKind::View => " (View)",
3337
pgt_schema_cache::TableKind::MaterializedView => " (M.View)",
3438
pgt_schema_cache::TableKind::Partitioned => " (Partitioned)",
3539
pgt_schema_cache::TableKind::Ordinary => "",
3640
};
3741

42+
write!(writer, "{}", table_kind)?;
43+
3844
let locked_txt = if self.rls_enabled {
3945
" - 🔒 RLS enabled"
4046
} else {
4147
" - 🔓 RLS disabled"
4248
};
4349

44-
write!(
45-
writer,
46-
"{}.{}{}{}",
47-
self.schema, self.name, table_kind, locked_txt
48-
)
50+
write!(writer, "{}", locked_txt)?;
51+
52+
Ok(())
4953
}
5054

5155
fn hover_body<W: Write>(&self, writer: &mut W) -> Result<bool, std::fmt::Error> {
@@ -73,7 +77,7 @@ impl ToHoverMarkdown for pgt_schema_cache::Column {
7377
fn hover_headline<W: Write>(&self, writer: &mut W) -> Result<(), std::fmt::Error> {
7478
write!(
7579
writer,
76-
"{}.{}.{}",
80+
"`{}.{}.{}`",
7781
self.schema_name, self.table_name, self.name
7882
)
7983
}
@@ -118,6 +122,100 @@ impl ToHoverMarkdown for pgt_schema_cache::Column {
118122
}
119123
}
120124

125+
impl ToHoverMarkdown for pgt_schema_cache::Function {
126+
fn hover_headline<W: Write>(&self, writer: &mut W) -> Result<(), std::fmt::Error> {
127+
write!(writer, "`{}.{}", self.schema, self.name)?;
128+
129+
if let Some(args) = &self.argument_types {
130+
write!(writer, "({})", args)?;
131+
} else {
132+
write!(writer, "()")?;
133+
}
134+
135+
write!(
136+
writer,
137+
" → {}`",
138+
self.return_type.as_ref().unwrap_or(&"void".to_string())
139+
)?;
140+
141+
Ok(())
142+
}
143+
144+
fn hover_body<W: Write>(&self, writer: &mut W) -> Result<bool, std::fmt::Error> {
145+
let kind_text = match self.kind {
146+
pgt_schema_cache::ProcKind::Function => "Function",
147+
pgt_schema_cache::ProcKind::Procedure => "Procedure",
148+
pgt_schema_cache::ProcKind::Aggregate => "Aggregate",
149+
pgt_schema_cache::ProcKind::Window => "Window",
150+
};
151+
152+
write!(writer, "{}", kind_text)?;
153+
154+
let behavior_text = match self.behavior {
155+
pgt_schema_cache::Behavior::Immutable => " - Immutable",
156+
pgt_schema_cache::Behavior::Stable => " - Stable",
157+
pgt_schema_cache::Behavior::Volatile => "",
158+
};
159+
160+
write!(writer, "{}", behavior_text)?;
161+
162+
if self.security_definer {
163+
write!(writer, " - Security DEFINER")?;
164+
} else {
165+
write!(writer, " - Security INVOKER")?;
166+
}
167+
168+
Ok(true)
169+
}
170+
171+
fn hover_footer<W: Write>(&self, writer: &mut W) -> Result<bool, std::fmt::Error> {
172+
if let Some(def) = self.definition.as_ref() {
173+
/*
174+
* We don't want to show 250 lines of functions to the user.
175+
* If we have more than 30 lines, we'll only show the signature.
176+
*/
177+
if def.lines().count() > 30 {
178+
let without_boilerplate: String = def
179+
.split_ascii_whitespace()
180+
.skip_while(|elem| {
181+
["create", "or", "replace", "function"]
182+
.contains(&elem.to_ascii_lowercase().as_str())
183+
})
184+
.collect::<Vec<&str>>()
185+
.join(" ");
186+
187+
for char in without_boilerplate.chars() {
188+
match char {
189+
'(' => {
190+
write!(writer, "(\n ")?;
191+
}
192+
193+
')' => {
194+
write!(writer, "\n)\n")?;
195+
break;
196+
}
197+
198+
',' => {
199+
// one space already present
200+
write!(writer, ",\n ")?;
201+
}
202+
203+
_ => {
204+
write!(writer, "{}", char)?;
205+
}
206+
}
207+
}
208+
} else {
209+
write!(writer, "```\n{}\n```", def)?;
210+
}
211+
212+
Ok(true)
213+
} else {
214+
Ok(false)
215+
}
216+
}
217+
}
218+
121219
fn markdown_newline<W: Write>(writer: &mut W) -> Result<(), std::fmt::Error> {
122220
write!(writer, " ")?;
123221
writeln!(writer)?;

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /