mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 05:44:56 +00:00
[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 https://github.com/astral-sh/ty-vscode/pull/112. closes: astral-sh/ty#472 ## Test Plan Add E2E tests.
This commit is contained in:
parent
c401a6d86e
commit
b22586fa0e
8 changed files with 181 additions and 20 deletions
|
@ -51,8 +51,13 @@ impl fmt::Display for DisplayInlayHint<'_, '_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inlay_hints(db: &dyn Db, file: File, range: TextRange) -> Vec<InlayHint<'_>> {
|
pub fn inlay_hints<'db>(
|
||||||
let mut visitor = InlayHintVisitor::new(db, file, range);
|
db: &'db dyn Db,
|
||||||
|
file: File,
|
||||||
|
range: TextRange,
|
||||||
|
settings: &InlayHintSettings,
|
||||||
|
) -> Vec<InlayHint<'db>> {
|
||||||
|
let mut visitor = InlayHintVisitor::new(db, file, range, settings);
|
||||||
|
|
||||||
let ast = parsed_module(db, file).load(db);
|
let ast = parsed_module(db, file).load(db);
|
||||||
|
|
||||||
|
@ -61,20 +66,34 @@ pub fn inlay_hints(db: &dyn Db, file: File, range: TextRange) -> Vec<InlayHint<'
|
||||||
visitor.hints
|
visitor.hints
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InlayHintVisitor<'db> {
|
/// Settings to control the behavior of inlay hints.
|
||||||
|
#[derive(Clone, Default, Debug)]
|
||||||
|
pub struct InlayHintSettings {
|
||||||
|
/// Whether to show variable type hints.
|
||||||
|
///
|
||||||
|
/// For example, this would enable / disable hints like the ones quoted below:
|
||||||
|
/// ```python
|
||||||
|
/// x": Literal[1]" = 1
|
||||||
|
/// ```
|
||||||
|
pub variable_types: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct InlayHintVisitor<'a, 'db> {
|
||||||
model: SemanticModel<'db>,
|
model: SemanticModel<'db>,
|
||||||
hints: Vec<InlayHint<'db>>,
|
hints: Vec<InlayHint<'db>>,
|
||||||
in_assignment: bool,
|
in_assignment: bool,
|
||||||
range: TextRange,
|
range: TextRange,
|
||||||
|
settings: &'a InlayHintSettings,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> InlayHintVisitor<'db> {
|
impl<'a, 'db> InlayHintVisitor<'a, 'db> {
|
||||||
fn new(db: &'db dyn Db, file: File, range: TextRange) -> Self {
|
fn new(db: &'db dyn Db, file: File, range: TextRange, settings: &'a InlayHintSettings) -> Self {
|
||||||
Self {
|
Self {
|
||||||
model: SemanticModel::new(db, file),
|
model: SemanticModel::new(db, file),
|
||||||
hints: Vec::new(),
|
hints: Vec::new(),
|
||||||
in_assignment: false,
|
in_assignment: false,
|
||||||
range,
|
range,
|
||||||
|
settings,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +105,7 @@ impl<'db> InlayHintVisitor<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourceOrderVisitor<'_> for InlayHintVisitor<'_> {
|
impl SourceOrderVisitor<'_> for InlayHintVisitor<'_, '_> {
|
||||||
fn enter_node(&mut self, node: AnyNodeRef<'_>) -> TraversalSignal {
|
fn enter_node(&mut self, node: AnyNodeRef<'_>) -> TraversalSignal {
|
||||||
if self.range.intersect(node.range()).is_some() {
|
if self.range.intersect(node.range()).is_some() {
|
||||||
TraversalSignal::Traverse
|
TraversalSignal::Traverse
|
||||||
|
@ -104,6 +123,10 @@ impl SourceOrderVisitor<'_> for InlayHintVisitor<'_> {
|
||||||
|
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Assign(assign) => {
|
Stmt::Assign(assign) => {
|
||||||
|
if !self.settings.variable_types {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self.in_assignment = true;
|
self.in_assignment = true;
|
||||||
for target in &assign.targets {
|
for target in &assign.targets {
|
||||||
self.visit_expr(target);
|
self.visit_expr(target);
|
||||||
|
@ -213,8 +236,21 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InlayHintTest {
|
impl InlayHintTest {
|
||||||
|
/// Returns the inlay hints for the given test case.
|
||||||
|
///
|
||||||
|
/// All inlay hints are generated using the applicable settings. Use
|
||||||
|
/// [`inlay_hints_with_settings`] to generate hints with custom settings.
|
||||||
|
///
|
||||||
|
/// [`inlay_hints_with_settings`]: Self::inlay_hints_with_settings
|
||||||
fn inlay_hints(&self) -> String {
|
fn inlay_hints(&self) -> String {
|
||||||
let hints = inlay_hints(&self.db, self.file, self.range);
|
self.inlay_hints_with_settings(&InlayHintSettings {
|
||||||
|
variable_types: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the inlay hints for the given test case with custom settings.
|
||||||
|
fn inlay_hints_with_settings(&self, settings: &InlayHintSettings) -> String {
|
||||||
|
let hints = inlay_hints(&self.db, self.file, self.range, settings);
|
||||||
|
|
||||||
let mut buf = source_text(&self.db, self.file).as_str().to_string();
|
let mut buf = source_text(&self.db, self.file).as_str().to_string();
|
||||||
|
|
||||||
|
@ -276,4 +312,18 @@ mod tests {
|
||||||
y = 2
|
y = 2
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn disabled_variable_types() {
|
||||||
|
let test = inlay_hint_test("x = 1");
|
||||||
|
|
||||||
|
assert_snapshot!(
|
||||||
|
test.inlay_hints_with_settings(&InlayHintSettings {
|
||||||
|
variable_types: false,
|
||||||
|
}),
|
||||||
|
@r"
|
||||||
|
x = 1
|
||||||
|
"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ pub use document_symbols::{document_symbols, document_symbols_with_options};
|
||||||
pub use goto::{goto_declaration, goto_definition, goto_type_definition};
|
pub use goto::{goto_declaration, goto_definition, goto_type_definition};
|
||||||
pub use goto_references::goto_references;
|
pub use goto_references::goto_references;
|
||||||
pub use hover::hover;
|
pub use hover::hover;
|
||||||
pub use inlay_hints::inlay_hints;
|
pub use inlay_hints::{InlayHintSettings, inlay_hints};
|
||||||
pub use markup::MarkupKind;
|
pub use markup::MarkupKind;
|
||||||
pub use references::ReferencesMode;
|
pub use references::ReferencesMode;
|
||||||
pub use rename::{can_rename, rename};
|
pub use rename::{can_rename, rename};
|
||||||
|
|
|
@ -47,7 +47,7 @@ impl BackgroundDocumentRequestHandler for InlayHintRequestHandler {
|
||||||
.range
|
.range
|
||||||
.to_text_range(&source, &index, snapshot.encoding());
|
.to_text_range(&source, &index, snapshot.encoding());
|
||||||
|
|
||||||
let inlay_hints = inlay_hints(db, file, range);
|
let inlay_hints = inlay_hints(db, file, range, snapshot.workspace_settings().inlay_hints());
|
||||||
|
|
||||||
let inlay_hints = inlay_hints
|
let inlay_hints = inlay_hints
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
|
@ -8,6 +8,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use ty_combine::Combine;
|
use ty_combine::Combine;
|
||||||
|
use ty_ide::InlayHintSettings;
|
||||||
use ty_project::metadata::Options as TyOptions;
|
use ty_project::metadata::Options as TyOptions;
|
||||||
use ty_project::metadata::options::ProjectOptionsOverrides;
|
use ty_project::metadata::options::ProjectOptionsOverrides;
|
||||||
use ty_project::metadata::value::{RangedValue, RelativePathBuf};
|
use ty_project::metadata::value::{RangedValue, RelativePathBuf};
|
||||||
|
@ -106,6 +107,15 @@ impl ClientOptions {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_variable_types_inlay_hints(mut self, variable_types: bool) -> Self {
|
||||||
|
self.workspace
|
||||||
|
.inlay_hints
|
||||||
|
.get_or_insert_default()
|
||||||
|
.variable_types = Some(variable_types);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_experimental_rename(mut self, enabled: bool) -> Self {
|
pub fn with_experimental_rename(mut self, enabled: bool) -> Self {
|
||||||
self.global.experimental.get_or_insert_default().rename = Some(enabled);
|
self.global.experimental.get_or_insert_default().rename = Some(enabled);
|
||||||
|
@ -138,7 +148,7 @@ impl GlobalOptions {
|
||||||
let experimental = self
|
let experimental = self
|
||||||
.experimental
|
.experimental
|
||||||
.map(|experimental| ExperimentalSettings {
|
.map(|experimental| ExperimentalSettings {
|
||||||
rename: experimental.rename.unwrap_or_default(),
|
rename: experimental.rename.unwrap_or(true),
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
@ -158,6 +168,9 @@ pub(crate) struct WorkspaceOptions {
|
||||||
/// Whether to disable language services like code completions, hover, etc.
|
/// Whether to disable language services like code completions, hover, etc.
|
||||||
disable_language_services: Option<bool>,
|
disable_language_services: Option<bool>,
|
||||||
|
|
||||||
|
/// Options to configure inlay hints.
|
||||||
|
inlay_hints: Option<InlayHintOptions>,
|
||||||
|
|
||||||
/// Information about the currently active Python environment in the VS Code Python extension.
|
/// Information about the currently active Python environment in the VS Code Python extension.
|
||||||
///
|
///
|
||||||
/// This is relevant only for VS Code and is populated by the ty VS Code extension.
|
/// This is relevant only for VS Code and is populated by the ty VS Code extension.
|
||||||
|
@ -211,11 +224,29 @@ impl WorkspaceOptions {
|
||||||
|
|
||||||
WorkspaceSettings {
|
WorkspaceSettings {
|
||||||
disable_language_services: self.disable_language_services.unwrap_or_default(),
|
disable_language_services: self.disable_language_services.unwrap_or_default(),
|
||||||
|
inlay_hints: self
|
||||||
|
.inlay_hints
|
||||||
|
.map(InlayHintOptions::into_settings)
|
||||||
|
.unwrap_or_default(),
|
||||||
overrides,
|
overrides,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Combine, Debug, Serialize, Deserialize, Default)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct InlayHintOptions {
|
||||||
|
variable_types: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InlayHintOptions {
|
||||||
|
fn into_settings(self) -> InlayHintSettings {
|
||||||
|
InlayHintSettings {
|
||||||
|
variable_types: self.variable_types.unwrap_or_default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Diagnostic mode for the language server.
|
/// Diagnostic mode for the language server.
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use super::options::DiagnosticMode;
|
use super::options::DiagnosticMode;
|
||||||
|
|
||||||
|
use ty_ide::InlayHintSettings;
|
||||||
use ty_project::metadata::options::ProjectOptionsOverrides;
|
use ty_project::metadata::options::ProjectOptionsOverrides;
|
||||||
|
|
||||||
/// Resolved client settings that are shared across all workspaces.
|
/// Resolved client settings that are shared across all workspaces.
|
||||||
|
@ -33,6 +34,7 @@ pub(crate) struct ExperimentalSettings {
|
||||||
#[derive(Clone, Default, Debug)]
|
#[derive(Clone, Default, Debug)]
|
||||||
pub(crate) struct WorkspaceSettings {
|
pub(crate) struct WorkspaceSettings {
|
||||||
pub(super) disable_language_services: bool,
|
pub(super) disable_language_services: bool,
|
||||||
|
pub(super) inlay_hints: InlayHintSettings,
|
||||||
pub(super) overrides: Option<ProjectOptionsOverrides>,
|
pub(super) overrides: Option<ProjectOptionsOverrides>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,4 +46,8 @@ impl WorkspaceSettings {
|
||||||
pub(crate) fn project_options_overrides(&self) -> Option<&ProjectOptionsOverrides> {
|
pub(crate) fn project_options_overrides(&self) -> Option<&ProjectOptionsOverrides> {
|
||||||
self.overrides.as_ref()
|
self.overrides.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn inlay_hints(&self) -> &InlayHintSettings {
|
||||||
|
&self.inlay_hints
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
38
crates/ty_server/tests/e2e/inlay_hints.rs
Normal file
38
crates/ty_server/tests/e2e/inlay_hints.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use lsp_types::{Position, Range, notification::PublishDiagnostics};
|
||||||
|
use ruff_db::system::SystemPath;
|
||||||
|
use ty_server::ClientOptions;
|
||||||
|
|
||||||
|
use crate::TestServerBuilder;
|
||||||
|
|
||||||
|
/// Tests that disabling variable types inlay hints works correctly.
|
||||||
|
#[test]
|
||||||
|
fn variable_inlay_hints_disabled() -> Result<()> {
|
||||||
|
let workspace_root = SystemPath::new("src");
|
||||||
|
let foo = SystemPath::new("src/foo.py");
|
||||||
|
let foo_content = "x = 1";
|
||||||
|
|
||||||
|
let mut server = TestServerBuilder::new()?
|
||||||
|
.with_initialization_options(
|
||||||
|
ClientOptions::default().with_variable_types_inlay_hints(false),
|
||||||
|
)
|
||||||
|
.with_workspace(workspace_root, None)?
|
||||||
|
.with_file(foo, foo_content)?
|
||||||
|
.enable_inlay_hints(true)
|
||||||
|
.build()?
|
||||||
|
.wait_until_workspaces_are_initialized()?;
|
||||||
|
|
||||||
|
server.open_text_document(foo, &foo_content, 1);
|
||||||
|
let _ = server.await_notification::<PublishDiagnostics>()?;
|
||||||
|
|
||||||
|
let hints = server
|
||||||
|
.inlay_hints_request(foo, Range::new(Position::new(0, 0), Position::new(0, 5)))?
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
hints.is_empty(),
|
||||||
|
"Expected no inlay hints, but found: {hints:?}"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -28,6 +28,7 @@
|
||||||
//! [`await_notification`]: TestServer::await_notification
|
//! [`await_notification`]: TestServer::await_notification
|
||||||
|
|
||||||
mod initialize;
|
mod initialize;
|
||||||
|
mod inlay_hints;
|
||||||
mod publish_diagnostics;
|
mod publish_diagnostics;
|
||||||
mod pull_diagnostics;
|
mod pull_diagnostics;
|
||||||
|
|
||||||
|
@ -48,20 +49,21 @@ use lsp_types::notification::{
|
||||||
Initialized, Notification,
|
Initialized, Notification,
|
||||||
};
|
};
|
||||||
use lsp_types::request::{
|
use lsp_types::request::{
|
||||||
DocumentDiagnosticRequest, HoverRequest, Initialize, Request, Shutdown, WorkspaceConfiguration,
|
DocumentDiagnosticRequest, HoverRequest, Initialize, InlayHintRequest, Request, Shutdown,
|
||||||
WorkspaceDiagnosticRequest,
|
WorkspaceConfiguration, WorkspaceDiagnosticRequest,
|
||||||
};
|
};
|
||||||
use lsp_types::{
|
use lsp_types::{
|
||||||
ClientCapabilities, ConfigurationParams, DiagnosticClientCapabilities,
|
ClientCapabilities, ConfigurationParams, DiagnosticClientCapabilities,
|
||||||
DidChangeTextDocumentParams, DidChangeWatchedFilesClientCapabilities,
|
DidChangeTextDocumentParams, DidChangeWatchedFilesClientCapabilities,
|
||||||
DidChangeWatchedFilesParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams,
|
DidChangeWatchedFilesParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams,
|
||||||
DocumentDiagnosticParams, DocumentDiagnosticReportResult, FileEvent, Hover, HoverParams,
|
DocumentDiagnosticParams, DocumentDiagnosticReportResult, FileEvent, Hover, HoverParams,
|
||||||
InitializeParams, InitializeResult, InitializedParams, NumberOrString, PartialResultParams,
|
InitializeParams, InitializeResult, InitializedParams, InlayHint, InlayHintClientCapabilities,
|
||||||
Position, PreviousResultId, PublishDiagnosticsClientCapabilities,
|
InlayHintParams, NumberOrString, PartialResultParams, Position, PreviousResultId,
|
||||||
TextDocumentClientCapabilities, TextDocumentContentChangeEvent, TextDocumentIdentifier,
|
PublishDiagnosticsClientCapabilities, Range, TextDocumentClientCapabilities,
|
||||||
TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier,
|
TextDocumentContentChangeEvent, TextDocumentIdentifier, TextDocumentItem,
|
||||||
WorkDoneProgressParams, WorkspaceClientCapabilities, WorkspaceDiagnosticParams,
|
TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, WorkDoneProgressParams,
|
||||||
WorkspaceDiagnosticReportResult, WorkspaceFolder,
|
WorkspaceClientCapabilities, WorkspaceDiagnosticParams, WorkspaceDiagnosticReportResult,
|
||||||
|
WorkspaceFolder,
|
||||||
};
|
};
|
||||||
use ruff_db::system::{OsSystem, SystemPath, SystemPathBuf, TestSystem};
|
use ruff_db::system::{OsSystem, SystemPath, SystemPathBuf, TestSystem};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
@ -725,6 +727,23 @@ impl TestServer {
|
||||||
let id = self.send_request::<HoverRequest>(params);
|
let id = self.send_request::<HoverRequest>(params);
|
||||||
self.await_response::<HoverRequest>(&id)
|
self.await_response::<HoverRequest>(&id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sends a `textDocument/inlayHint` request for the document at the given path and range.
|
||||||
|
pub(crate) fn inlay_hints_request(
|
||||||
|
&mut self,
|
||||||
|
path: impl AsRef<SystemPath>,
|
||||||
|
range: Range,
|
||||||
|
) -> Result<Option<Vec<InlayHint>>> {
|
||||||
|
let params = InlayHintParams {
|
||||||
|
text_document: TextDocumentIdentifier {
|
||||||
|
uri: self.file_uri(path),
|
||||||
|
},
|
||||||
|
range,
|
||||||
|
work_done_progress_params: WorkDoneProgressParams::default(),
|
||||||
|
};
|
||||||
|
let id = self.send_request::<InlayHintRequest>(params);
|
||||||
|
self.await_response::<InlayHintRequest>(&id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for TestServer {
|
impl fmt::Debug for TestServer {
|
||||||
|
@ -908,6 +927,19 @@ impl TestServerBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enable or disable inlay hints capability
|
||||||
|
pub(crate) fn enable_inlay_hints(mut self, enabled: bool) -> Self {
|
||||||
|
self.client_capabilities
|
||||||
|
.text_document
|
||||||
|
.get_or_insert_default()
|
||||||
|
.inlay_hint = if enabled {
|
||||||
|
Some(InlayHintClientCapabilities::default())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Enable or disable file watching capability
|
/// Enable or disable file watching capability
|
||||||
#[expect(dead_code)]
|
#[expect(dead_code)]
|
||||||
pub(crate) fn enable_did_change_watched_files(mut self, enabled: bool) -> Self {
|
pub(crate) fn enable_did_change_watched_files(mut self, enabled: bool) -> Self {
|
||||||
|
|
|
@ -16,8 +16,8 @@ use ruff_python_formatter::formatted_file;
|
||||||
use ruff_source_file::{LineIndex, OneIndexed, SourceLocation};
|
use ruff_source_file::{LineIndex, OneIndexed, SourceLocation};
|
||||||
use ruff_text_size::{Ranged, TextSize};
|
use ruff_text_size::{Ranged, TextSize};
|
||||||
use ty_ide::{
|
use ty_ide::{
|
||||||
MarkupKind, RangedValue, document_highlights, goto_declaration, goto_definition,
|
InlayHintSettings, MarkupKind, RangedValue, document_highlights, goto_declaration,
|
||||||
goto_references, goto_type_definition, hover, inlay_hints,
|
goto_definition, goto_references, goto_type_definition, hover, inlay_hints,
|
||||||
};
|
};
|
||||||
use ty_ide::{NavigationTargets, signature_help};
|
use ty_ide::{NavigationTargets, signature_help};
|
||||||
use ty_project::metadata::options::Options;
|
use ty_project::metadata::options::Options;
|
||||||
|
@ -435,6 +435,10 @@ impl Workspace {
|
||||||
&self.db,
|
&self.db,
|
||||||
file_id.file,
|
file_id.file,
|
||||||
range.to_text_range(&index, &source, self.position_encoding)?,
|
range.to_text_range(&index, &source, self.position_encoding)?,
|
||||||
|
// TODO: Provide a way to configure this
|
||||||
|
&InlayHintSettings {
|
||||||
|
variable_types: true,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(result
|
Ok(result
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue