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

feat: basic hover #463

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
juleswritescode merged 24 commits into main from feat/on-hover
Aug 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
768f92b
intermeidate
juleswritescode Jul 18, 2025
4d9d35c
add some formatting
juleswritescode Jul 19, 2025
50d789d
so far
juleswritescode Jul 19, 2025
a80ba4c
ok
juleswritescode Jul 19, 2025
d50db86
some more refatorings
juleswritescode Jul 19, 2025
70cccba
ok
juleswritescode Jul 19, 2025
b8367c5
almost there
juleswritescode Jul 19, 2025
563cdcf
another...
juleswritescode Jul 19, 2025
bfc2404
wowa wiwa
juleswritescode Jul 19, 2025
fee1ccf
ok
juleswritescode Jul 19, 2025
78b8152
renamed it
juleswritescode Jul 19, 2025
64910d8
Merge branch 'refactor/extract-ts-context' of https://github.com/supa...
juleswritescode Jul 19, 2025
986eb29
remove schema caceh
juleswritescode Jul 19, 2025
f27cde5
Merge branch 'refactor/extract-ts-context' of https://github.com/supa...
juleswritescode Jul 19, 2025
97f42fc
ok?
juleswritescode Jul 19, 2025
a8c7051
wowa wiwa
juleswritescode Jul 19, 2025
a065011
just ready
juleswritescode Jul 19, 2025
25d7d31
ok?
juleswritescode Aug 14, 2025
dbc3c7d
fixed conflicts
juleswritescode Aug 14, 2025
606bd40
ok?
juleswritescode Aug 14, 2025
07e56b1
remove libpg again
juleswritescode Aug 14, 2025
01e7476
no underscore
juleswritescode Aug 14, 2025
ff58d26
thats not a warning
juleswritescode Aug 14, 2025
2dc68ba
Merge branch 'main' into feat/on-hover
juleswritescode Aug 18, 2025
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
30 changes: 30 additions & 0 deletions Cargo.lock
View file Open in desktop

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ pgt_diagnostics_categories = { path = "./crates/pgt_diagnostics_categories", ver
pgt_diagnostics_macros = { path = "./crates/pgt_diagnostics_macros", version = "0.0.0" }
pgt_flags = { path = "./crates/pgt_flags", version = "0.0.0" }
pgt_fs = { path = "./crates/pgt_fs", version = "0.0.0" }
pgt_hover = { path = "./crates/pgt_hover", version = "0.0.0" }
pgt_lexer = { path = "./crates/pgt_lexer", version = "0.0.0" }
pgt_lexer_codegen = { path = "./crates/pgt_lexer_codegen", version = "0.0.0" }
pgt_lsp = { path = "./crates/pgt_lsp", version = "0.0.0" }
Expand Down
36 changes: 36 additions & 0 deletions crates/pgt_hover/Cargo.toml
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[package]
authors.workspace = true
categories.workspace = true
description = "<DESCRIPTION>"
edition.workspace = true
homepage.workspace = true
keywords.workspace = true
license.workspace = true
name = "pgt_hover"
repository.workspace = true
version = "0.0.0"


[dependencies]
humansize = { version = "2.1.3" }
pgt_query.workspace = true
pgt_schema_cache.workspace = true
pgt_text_size.workspace = true
pgt_treesitter.workspace = true
schemars = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
sqlx.workspace = true
tokio = { version = "1.41.1", features = ["full"] }
tracing = { workspace = true }
tree-sitter.workspace = true
tree_sitter_sql.workspace = true

[dev-dependencies]
pgt_test_utils.workspace = true

[lib]
doctest = false

[features]
schema = ["dep:schemars"]
50 changes: 50 additions & 0 deletions crates/pgt_hover/src/hovered_node.rs
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use pgt_text_size::TextSize;
use pgt_treesitter::TreeSitterContextParams;

#[derive(Debug)]
pub(crate) enum NodeIdentification {
Name(String),
SchemaAndName((String, String)),
#[allow(unused)]
SchemaAndTableAndName((String, String, String)),
}

#[allow(unused)]
#[derive(Debug)]
pub(crate) enum HoveredNode {
Schema(NodeIdentification),
Table(NodeIdentification),
Function(NodeIdentification),
Column(NodeIdentification),
Policy(NodeIdentification),
Trigger(NodeIdentification),
Role(NodeIdentification),
}

impl HoveredNode {
pub(crate) fn get(position: TextSize, text: &str, tree: &tree_sitter::Tree) -> Option<Self> {
let ctx = pgt_treesitter::context::TreesitterContext::new(TreeSitterContextParams {
position,
text,
tree,
});

let node_content = ctx.get_node_under_cursor_content()?;

let under_node = ctx.node_under_cursor.as_ref()?;

match under_node.kind() {
"identifier" if ctx.parent_matches_one_of_kind(&["object_reference", "relation"]) => {
if let Some(schema) = ctx.schema_or_alias_name {
Some(HoveredNode::Table(NodeIdentification::SchemaAndName((
schema,
node_content,
))))
} else {
Some(HoveredNode::Table(NodeIdentification::Name(node_content)))
}
}
_ => None,
}
}
}
47 changes: 47 additions & 0 deletions crates/pgt_hover/src/lib.rs
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use pgt_schema_cache::SchemaCache;
use pgt_text_size::TextSize;

use crate::{hovered_node::HoveredNode, to_markdown::ToHoverMarkdown};

mod hovered_node;
mod to_markdown;

pub struct OnHoverParams<'a> {
pub position: TextSize,
pub schema_cache: &'a SchemaCache,
pub stmt_sql: &'a str,
pub ast: Option<&'a pgt_query::NodeEnum>,
pub ts_tree: &'a tree_sitter::Tree,
}

pub fn on_hover(params: OnHoverParams) -> Vec<String> {
if let Some(hovered_node) = HoveredNode::get(params.position, params.stmt_sql, params.ts_tree) {
match hovered_node {
HoveredNode::Table(node_identification) => {
let table = match node_identification {
hovered_node::NodeIdentification::Name(n) => {
params.schema_cache.find_table(n.as_str(), None)
}
hovered_node::NodeIdentification::SchemaAndName((s, n)) => {
params.schema_cache.find_table(n.as_str(), Some(s.as_str()))
}
hovered_node::NodeIdentification::SchemaAndTableAndName(_) => None,
};

table
.map(|t| {
let mut markdown = String::new();
match t.to_hover_markdown(&mut markdown) {
Ok(_) => vec![markdown],
Err(_) => vec![],
}
})
.unwrap_or(vec![])
}

_ => todo!(),
}
} else {
Default::default()
}
}
90 changes: 90 additions & 0 deletions crates/pgt_hover/src/to_markdown.rs
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use std::fmt::Write;

use humansize::DECIMAL;

pub(crate) trait ToHoverMarkdown {
fn to_hover_markdown<W: Write>(&self, writer: &mut W) -> Result<(), std::fmt::Error>;
}

impl ToHoverMarkdown for pgt_schema_cache::Table {
fn to_hover_markdown<W: Write>(&self, writer: &mut W) -> Result<(), std::fmt::Error> {
HeadlineWriter::for_table(writer, self)?;
BodyWriter::for_table(writer, self)?;
FooterWriter::for_table(writer, self)?;

Ok(())
}
}

struct HeadlineWriter;

impl HeadlineWriter {
fn for_table<W: Write>(
writer: &mut W,
table: &pgt_schema_cache::Table,
) -> Result<(), std::fmt::Error> {
let table_kind = match table.table_kind {
pgt_schema_cache::TableKind::View => " (View)",
pgt_schema_cache::TableKind::MaterializedView => " (M.View)",
pgt_schema_cache::TableKind::Partitioned => " (Partitioned)",
pgt_schema_cache::TableKind::Ordinary => "",
};

let locked_txt = if table.rls_enabled {
" - 🔒 RLS enabled"
} else {
" - 🔓 RLS disabled"
};

write!(
writer,
"### {}.{}{}{}",
table.schema, table.name, table_kind, locked_txt
)?;

markdown_newline(writer)?;

Ok(())
}
}

struct BodyWriter;

impl BodyWriter {
fn for_table<W: Write>(
writer: &mut W,
table: &pgt_schema_cache::Table,
) -> Result<(), std::fmt::Error> {
if let Some(c) = table.comment.as_ref() {
write!(writer, "{}", c)?;
markdown_newline(writer)?;
}

Ok(())
}
}

struct FooterWriter;

impl FooterWriter {
fn for_table<W: Write>(
writer: &mut W,
table: &pgt_schema_cache::Table,
) -> Result<(), std::fmt::Error> {
write!(
writer,
"~{} rows, ~{} dead rows, {}",
table.live_rows_estimate,
table.dead_rows_estimate,
humansize::format_size(table.bytes as u64, DECIMAL)
)?;

Ok(())
}
}

fn markdown_newline<W: Write>(writer: &mut W) -> Result<(), std::fmt::Error> {
write!(writer, " ")?;
writeln!(writer)?;
Ok(())
}
8 changes: 5 additions & 3 deletions crates/pgt_lsp/src/capabilities.rs
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ use crate::handlers::code_actions::command_id;
use pgt_workspace::features::code_actions::CommandActionCategory;
use strum::IntoEnumIterator;
use tower_lsp::lsp_types::{
ClientCapabilities, CompletionOptions, ExecuteCommandOptions, PositionEncodingKind,
SaveOptions, ServerCapabilities, TextDocumentSyncCapability, TextDocumentSyncKind,
TextDocumentSyncOptions, TextDocumentSyncSaveOptions, WorkDoneProgressOptions,
ClientCapabilities, CompletionOptions, ExecuteCommandOptions, HoverProviderCapability,
PositionEncodingKind, SaveOptions, ServerCapabilities, TextDocumentSyncCapability,
TextDocumentSyncKind, TextDocumentSyncOptions, TextDocumentSyncSaveOptions,
WorkDoneProgressOptions,
};

/// The capabilities to send from server as part of [`InitializeResult`]
Expand Down Expand Up @@ -62,6 +63,7 @@ pub(crate) fn server_capabilities(capabilities: &ClientCapabilities) -> ServerCa
true,
)),
rename_provider: None,
hover_provider: Some(HoverProviderCapability::Simple(true)),
..Default::default()
}
}
1 change: 1 addition & 0 deletions crates/pgt_lsp/src/handlers.rs
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub(crate) mod code_actions;
pub(crate) mod completions;
pub(crate) mod hover;
pub(crate) mod text_document;
42 changes: 42 additions & 0 deletions crates/pgt_lsp/src/handlers/hover.rs
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use pgt_workspace::{WorkspaceError, features::on_hover::OnHoverParams};
use tower_lsp::lsp_types::{self, MarkedString, MarkupContent};

use crate::{adapters::get_cursor_position, diagnostics::LspError, session::Session};

pub(crate) fn on_hover(
session: &Session,
params: lsp_types::HoverParams,
) -> Result<lsp_types::HoverContents, LspError> {
let url = params.text_document_position_params.text_document.uri;
let position = params.text_document_position_params.position;
let path = session.file_path(&url)?;

match session.workspace.on_hover(OnHoverParams {
path,
position: get_cursor_position(session, &url, position)?,
}) {
Ok(result) => {
tracing::debug!("Found hover items: {:#?}", result);

Ok(lsp_types::HoverContents::Array(
result
.into_iter()
.map(MarkedString::from_markdown)
.collect(),
))
}

Err(e) => match e {
WorkspaceError::DatabaseConnectionError(_) => {
Ok(lsp_types::HoverContents::Markup(MarkupContent {
kind: lsp_types::MarkupKind::PlainText,
value: "Cannot connect to database.".into(),
}))
}
_ => {
tracing::error!("Received an error: {:#?}", e);
Err(e.into())
}
},
}
}
11 changes: 11 additions & 0 deletions crates/pgt_lsp/src/server.rs
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,17 @@ impl LanguageServer for LSPServer {
}
}

#[tracing::instrument(level = "trace", skip_all)]
async fn hover(&self, params: HoverParams) -> LspResult<Option<Hover>> {
match handlers::hover::on_hover(&self.session, params) {
Ok(result) => LspResult::Ok(Some(Hover {
contents: result,
range: None,
})),
Err(e) => LspResult::Err(into_lsp_error(e)),
}
}

#[tracing::instrument(level = "trace", skip_all)]
async fn completion(&self, params: CompletionParams) -> LspResult<Option<CompletionResponse>> {
match handlers::completions::get_completions(&self.session, params) {
Expand Down
6 changes: 3 additions & 3 deletions crates/pgt_schema_cache/src/schema_cache.rs
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@ impl SchemaCache {
pub fn find_table(&self, name: &str, schema: Option<&str>) -> Option<&Table> {
self.tables
.iter()
.find(|t| t.name == name && schema.is_none() || Some(t.schema.as_str()) == schema)
.find(|t| t.name == name && schema.is_none_or(|s| s == t.schema.as_str()))
}

pub fn find_type(&self, name: &str, schema: Option<&str>) -> Option<&PostgresType> {
self.types
.iter()
.find(|t| t.name == name && schema.is_none() || Some(t.schema.as_str()) == schema)
.find(|t| t.name == name && schema.is_none_or(|s| s == t.schema.as_str()))
}

pub fn find_col(&self, name: &str, table: &str, schema: Option<&str>) -> Option<&Column> {
Expand All @@ -86,7 +86,7 @@ impl SchemaCache {
pub fn find_types(&self, name: &str, schema: Option<&str>) -> Vec<&PostgresType> {
self.types
.iter()
.filter(|t| t.name == name && schema.is_none() || Some(t.schema.as_str()) == schema)
.filter(|t| t.name == name && schema.is_none_or(|s| s == t.schema.as_str()))
.collect()
}
}
Expand Down
Loading

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