Skip to content

Commit b22586f

Browse files
authored
[ty] Add ty.inlayHints.variableTypes server option (#19780)
## Summary This PR adds a new `ty.inlayHints.variableTypes` server setting to configure ty to include / exclude inlay hints at variable position. Currently, we only support inlay hints at this position so this option basically translates to enabling / disabling inlay hints for now :) The VS Code extension PR is astral-sh/ty-vscode#112. closes: astral-sh/ty#472 ## Test Plan Add E2E tests.
1 parent c401a6d commit b22586f

File tree

8 files changed

+181
-20
lines changed

8 files changed

+181
-20
lines changed

crates/ty_ide/src/inlay_hints.rs

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,13 @@ impl fmt::Display for DisplayInlayHint<'_, '_> {
5151
}
5252
}
5353

54-
pub fn inlay_hints(db: &dyn Db, file: File, range: TextRange) -> Vec<InlayHint<'_>> {
55-
let mut visitor = InlayHintVisitor::new(db, file, range);
54+
pub fn inlay_hints<'db>(
55+
db: &'db dyn Db,
56+
file: File,
57+
range: TextRange,
58+
settings: &InlayHintSettings,
59+
) -> Vec<InlayHint<'db>> {
60+
let mut visitor = InlayHintVisitor::new(db, file, range, settings);
5661

5762
let ast = parsed_module(db, file).load(db);
5863

@@ -61,20 +66,34 @@ pub fn inlay_hints(db: &dyn Db, file: File, range: TextRange) -> Vec<InlayHint<'
6166
visitor.hints
6267
}
6368

64-
struct InlayHintVisitor<'db> {
69+
/// Settings to control the behavior of inlay hints.
70+
#[derive(Clone, Default, Debug)]
71+
pub struct InlayHintSettings {
72+
/// Whether to show variable type hints.
73+
///
74+
/// For example, this would enable / disable hints like the ones quoted below:
75+
/// ```python
76+
/// x": Literal[1]" = 1
77+
/// ```
78+
pub variable_types: bool,
79+
}
80+
81+
struct InlayHintVisitor<'a, 'db> {
6582
model: SemanticModel<'db>,
6683
hints: Vec<InlayHint<'db>>,
6784
in_assignment: bool,
6885
range: TextRange,
86+
settings: &'a InlayHintSettings,
6987
}
7088

71-
impl<'db> InlayHintVisitor<'db> {
72-
fn new(db: &'db dyn Db, file: File, range: TextRange) -> Self {
89+
impl<'a, 'db> InlayHintVisitor<'a, 'db> {
90+
fn new(db: &'db dyn Db, file: File, range: TextRange, settings: &'a InlayHintSettings) -> Self {
7391
Self {
7492
model: SemanticModel::new(db, file),
7593
hints: Vec::new(),
7694
in_assignment: false,
7795
range,
96+
settings,
7897
}
7998
}
8099

@@ -86,7 +105,7 @@ impl<'db> InlayHintVisitor<'db> {
86105
}
87106
}
88107

89-
impl SourceOrderVisitor<'_> for InlayHintVisitor<'_> {
108+
impl SourceOrderVisitor<'_> for InlayHintVisitor<'_, '_> {
90109
fn enter_node(&mut self, node: AnyNodeRef<'_>) -> TraversalSignal {
91110
if self.range.intersect(node.range()).is_some() {
92111
TraversalSignal::Traverse
@@ -104,6 +123,10 @@ impl SourceOrderVisitor<'_> for InlayHintVisitor<'_> {
104123

105124
match stmt {
106125
Stmt::Assign(assign) => {
126+
if !self.settings.variable_types {
127+
return;
128+
}
129+
107130
self.in_assignment = true;
108131
for target in &assign.targets {
109132
self.visit_expr(target);
@@ -213,8 +236,21 @@ mod tests {
213236
}
214237

215238
impl InlayHintTest {
239+
/// Returns the inlay hints for the given test case.
240+
///
241+
/// All inlay hints are generated using the applicable settings. Use
242+
/// [`inlay_hints_with_settings`] to generate hints with custom settings.
243+
///
244+
/// [`inlay_hints_with_settings`]: Self::inlay_hints_with_settings
216245
fn inlay_hints(&self) -> String {
217-
let hints = inlay_hints(&self.db, self.file, self.range);
246+
self.inlay_hints_with_settings(&InlayHintSettings {
247+
variable_types: true,
248+
})
249+
}
250+
251+
/// Returns the inlay hints for the given test case with custom settings.
252+
fn inlay_hints_with_settings(&self, settings: &InlayHintSettings) -> String {
253+
let hints = inlay_hints(&self.db, self.file, self.range, settings);
218254

219255
let mut buf = source_text(&self.db, self.file).as_str().to_string();
220256

@@ -276,4 +312,18 @@ mod tests {
276312
y = 2
277313
");
278314
}
315+
316+
#[test]
317+
fn disabled_variable_types() {
318+
let test = inlay_hint_test("x = 1");
319+
320+
assert_snapshot!(
321+
test.inlay_hints_with_settings(&InlayHintSettings {
322+
variable_types: false,
323+
}),
324+
@r"
325+
x = 1
326+
"
327+
);
328+
}
279329
}

crates/ty_ide/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub use document_symbols::{document_symbols, document_symbols_with_options};
2727
pub use goto::{goto_declaration, goto_definition, goto_type_definition};
2828
pub use goto_references::goto_references;
2929
pub use hover::hover;
30-
pub use inlay_hints::inlay_hints;
30+
pub use inlay_hints::{InlayHintSettings, inlay_hints};
3131
pub use markup::MarkupKind;
3232
pub use references::ReferencesMode;
3333
pub use rename::{can_rename, rename};

crates/ty_server/src/server/api/requests/inlay_hints.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ impl BackgroundDocumentRequestHandler for InlayHintRequestHandler {
4747
.range
4848
.to_text_range(&source, &index, snapshot.encoding());
4949

50-
let inlay_hints = inlay_hints(db, file, range);
50+
let inlay_hints = inlay_hints(db, file, range, snapshot.workspace_settings().inlay_hints());
5151

5252
let inlay_hints = inlay_hints
5353
.into_iter()

crates/ty_server/src/session/options.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use serde::{Deserialize, Serialize};
88
use serde_json::Value;
99

1010
use ty_combine::Combine;
11+
use ty_ide::InlayHintSettings;
1112
use ty_project::metadata::Options as TyOptions;
1213
use ty_project::metadata::options::ProjectOptionsOverrides;
1314
use ty_project::metadata::value::{RangedValue, RelativePathBuf};
@@ -106,6 +107,15 @@ impl ClientOptions {
106107
self
107108
}
108109

110+
#[must_use]
111+
pub fn with_variable_types_inlay_hints(mut self, variable_types: bool) -> Self {
112+
self.workspace
113+
.inlay_hints
114+
.get_or_insert_default()
115+
.variable_types = Some(variable_types);
116+
self
117+
}
118+
109119
#[must_use]
110120
pub fn with_experimental_rename(mut self, enabled: bool) -> Self {
111121
self.global.experimental.get_or_insert_default().rename = Some(enabled);
@@ -138,7 +148,7 @@ impl GlobalOptions {
138148
let experimental = self
139149
.experimental
140150
.map(|experimental| ExperimentalSettings {
141-
rename: experimental.rename.unwrap_or_default(),
151+
rename: experimental.rename.unwrap_or(true),
142152
})
143153
.unwrap_or_default();
144154

@@ -158,6 +168,9 @@ pub(crate) struct WorkspaceOptions {
158168
/// Whether to disable language services like code completions, hover, etc.
159169
disable_language_services: Option<bool>,
160170

171+
/// Options to configure inlay hints.
172+
inlay_hints: Option<InlayHintOptions>,
173+
161174
/// Information about the currently active Python environment in the VS Code Python extension.
162175
///
163176
/// This is relevant only for VS Code and is populated by the ty VS Code extension.
@@ -211,11 +224,29 @@ impl WorkspaceOptions {
211224

212225
WorkspaceSettings {
213226
disable_language_services: self.disable_language_services.unwrap_or_default(),
227+
inlay_hints: self
228+
.inlay_hints
229+
.map(InlayHintOptions::into_settings)
230+
.unwrap_or_default(),
214231
overrides,
215232
}
216233
}
217234
}
218235

236+
#[derive(Clone, Combine, Debug, Serialize, Deserialize, Default)]
237+
#[serde(rename_all = "camelCase")]
238+
struct InlayHintOptions {
239+
variable_types: Option<bool>,
240+
}
241+
242+
impl InlayHintOptions {
243+
fn into_settings(self) -> InlayHintSettings {
244+
InlayHintSettings {
245+
variable_types: self.variable_types.unwrap_or_default(),
246+
}
247+
}
248+
}
249+
219250
/// Diagnostic mode for the language server.
220251
#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
221252
#[serde(rename_all = "camelCase")]

crates/ty_server/src/session/settings.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use super::options::DiagnosticMode;
22

3+
use ty_ide::InlayHintSettings;
34
use ty_project::metadata::options::ProjectOptionsOverrides;
45

56
/// Resolved client settings that are shared across all workspaces.
@@ -33,6 +34,7 @@ pub(crate) struct ExperimentalSettings {
3334
#[derive(Clone, Default, Debug)]
3435
pub(crate) struct WorkspaceSettings {
3536
pub(super) disable_language_services: bool,
37+
pub(super) inlay_hints: InlayHintSettings,
3638
pub(super) overrides: Option<ProjectOptionsOverrides>,
3739
}
3840

@@ -44,4 +46,8 @@ impl WorkspaceSettings {
4446
pub(crate) fn project_options_overrides(&self) -> Option<&ProjectOptionsOverrides> {
4547
self.overrides.as_ref()
4648
}
49+
50+
pub(crate) fn inlay_hints(&self) -> &InlayHintSettings {
51+
&self.inlay_hints
52+
}
4753
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use anyhow::Result;
2+
use lsp_types::{Position, Range, notification::PublishDiagnostics};
3+
use ruff_db::system::SystemPath;
4+
use ty_server::ClientOptions;
5+
6+
use crate::TestServerBuilder;
7+
8+
/// Tests that disabling variable types inlay hints works correctly.
9+
#[test]
10+
fn variable_inlay_hints_disabled() -> Result<()> {
11+
let workspace_root = SystemPath::new("src");
12+
let foo = SystemPath::new("src/foo.py");
13+
let foo_content = "x = 1";
14+
15+
let mut server = TestServerBuilder::new()?
16+
.with_initialization_options(
17+
ClientOptions::default().with_variable_types_inlay_hints(false),
18+
)
19+
.with_workspace(workspace_root, None)?
20+
.with_file(foo, foo_content)?
21+
.enable_inlay_hints(true)
22+
.build()?
23+
.wait_until_workspaces_are_initialized()?;
24+
25+
server.open_text_document(foo, &foo_content, 1);
26+
let _ = server.await_notification::<PublishDiagnostics>()?;
27+
28+
let hints = server
29+
.inlay_hints_request(foo, Range::new(Position::new(0, 0), Position::new(0, 5)))?
30+
.unwrap();
31+
32+
assert!(
33+
hints.is_empty(),
34+
"Expected no inlay hints, but found: {hints:?}"
35+
);
36+
37+
Ok(())
38+
}

crates/ty_server/tests/e2e/main.rs

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
//! [`await_notification`]: TestServer::await_notification
2929
3030
mod initialize;
31+
mod inlay_hints;
3132
mod publish_diagnostics;
3233
mod pull_diagnostics;
3334

@@ -48,20 +49,21 @@ use lsp_types::notification::{
4849
Initialized, Notification,
4950
};
5051
use lsp_types::request::{
51-
DocumentDiagnosticRequest, HoverRequest, Initialize, Request, Shutdown, WorkspaceConfiguration,
52-
WorkspaceDiagnosticRequest,
52+
DocumentDiagnosticRequest, HoverRequest, Initialize, InlayHintRequest, Request, Shutdown,
53+
WorkspaceConfiguration, WorkspaceDiagnosticRequest,
5354
};
5455
use lsp_types::{
5556
ClientCapabilities, ConfigurationParams, DiagnosticClientCapabilities,
5657
DidChangeTextDocumentParams, DidChangeWatchedFilesClientCapabilities,
5758
DidChangeWatchedFilesParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams,
5859
DocumentDiagnosticParams, DocumentDiagnosticReportResult, FileEvent, Hover, HoverParams,
59-
InitializeParams, InitializeResult, InitializedParams, NumberOrString, PartialResultParams,
60-
Position, PreviousResultId, PublishDiagnosticsClientCapabilities,
61-
TextDocumentClientCapabilities, TextDocumentContentChangeEvent, TextDocumentIdentifier,
62-
TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier,
63-
WorkDoneProgressParams, WorkspaceClientCapabilities, WorkspaceDiagnosticParams,
64-
WorkspaceDiagnosticReportResult, WorkspaceFolder,
60+
InitializeParams, InitializeResult, InitializedParams, InlayHint, InlayHintClientCapabilities,
61+
InlayHintParams, NumberOrString, PartialResultParams, Position, PreviousResultId,
62+
PublishDiagnosticsClientCapabilities, Range, TextDocumentClientCapabilities,
63+
TextDocumentContentChangeEvent, TextDocumentIdentifier, TextDocumentItem,
64+
TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, WorkDoneProgressParams,
65+
WorkspaceClientCapabilities, WorkspaceDiagnosticParams, WorkspaceDiagnosticReportResult,
66+
WorkspaceFolder,
6567
};
6668
use ruff_db::system::{OsSystem, SystemPath, SystemPathBuf, TestSystem};
6769
use rustc_hash::FxHashMap;
@@ -725,6 +727,23 @@ impl TestServer {
725727
let id = self.send_request::<HoverRequest>(params);
726728
self.await_response::<HoverRequest>(&id)
727729
}
730+
731+
/// Sends a `textDocument/inlayHint` request for the document at the given path and range.
732+
pub(crate) fn inlay_hints_request(
733+
&mut self,
734+
path: impl AsRef<SystemPath>,
735+
range: Range,
736+
) -> Result<Option<Vec<InlayHint>>> {
737+
let params = InlayHintParams {
738+
text_document: TextDocumentIdentifier {
739+
uri: self.file_uri(path),
740+
},
741+
range,
742+
work_done_progress_params: WorkDoneProgressParams::default(),
743+
};
744+
let id = self.send_request::<InlayHintRequest>(params);
745+
self.await_response::<InlayHintRequest>(&id)
746+
}
728747
}
729748

730749
impl fmt::Debug for TestServer {
@@ -908,6 +927,19 @@ impl TestServerBuilder {
908927
self
909928
}
910929

930+
/// Enable or disable inlay hints capability
931+
pub(crate) fn enable_inlay_hints(mut self, enabled: bool) -> Self {
932+
self.client_capabilities
933+
.text_document
934+
.get_or_insert_default()
935+
.inlay_hint = if enabled {
936+
Some(InlayHintClientCapabilities::default())
937+
} else {
938+
None
939+
};
940+
self
941+
}
942+
911943
/// Enable or disable file watching capability
912944
#[expect(dead_code)]
913945
pub(crate) fn enable_did_change_watched_files(mut self, enabled: bool) -> Self {

crates/ty_wasm/src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ use ruff_python_formatter::formatted_file;
1616
use ruff_source_file::{LineIndex, OneIndexed, SourceLocation};
1717
use ruff_text_size::{Ranged, TextSize};
1818
use ty_ide::{
19-
MarkupKind, RangedValue, document_highlights, goto_declaration, goto_definition,
20-
goto_references, goto_type_definition, hover, inlay_hints,
19+
InlayHintSettings, MarkupKind, RangedValue, document_highlights, goto_declaration,
20+
goto_definition, goto_references, goto_type_definition, hover, inlay_hints,
2121
};
2222
use ty_ide::{NavigationTargets, signature_help};
2323
use ty_project::metadata::options::Options;
@@ -435,6 +435,10 @@ impl Workspace {
435435
&self.db,
436436
file_id.file,
437437
range.to_text_range(&index, &source, self.position_encoding)?,
438+
// TODO: Provide a way to configure this
439+
&InlayHintSettings {
440+
variable_types: true,
441+
},
438442
);
439443

440444
Ok(result

0 commit comments

Comments
 (0)