mirror of https://github.com/astral-sh/ruff
[ty] Add goto definition to playground (#19425)
This commit is contained in:
parent
06f9f52e59
commit
98d1811dd1
|
|
@ -4,7 +4,7 @@ use js_sys::{Error, JsString};
|
|||
use ruff_db::Db as _;
|
||||
use ruff_db::diagnostic::{self, DisplayDiagnosticConfig};
|
||||
use ruff_db::files::{File, FileRange, system_path_to_file};
|
||||
use ruff_db::source::{line_index, source_text};
|
||||
use ruff_db::source::{SourceText, line_index, source_text};
|
||||
use ruff_db::system::walk_directory::WalkDirectoryBuilder;
|
||||
use ruff_db::system::{
|
||||
CaseSensitivity, DirectoryEntry, GlobError, MemoryFileSystem, Metadata, PatternError, System,
|
||||
|
|
@ -14,8 +14,11 @@ use ruff_notebook::Notebook;
|
|||
use ruff_python_formatter::formatted_file;
|
||||
use ruff_source_file::{LineIndex, OneIndexed, SourceLocation};
|
||||
use ruff_text_size::{Ranged, TextSize};
|
||||
use ty_ide::signature_help;
|
||||
use ty_ide::{MarkupKind, goto_type_definition, hover, inlay_hints};
|
||||
use ty_ide::{
|
||||
MarkupKind, RangedValue, goto_declaration, goto_definition, goto_type_definition, hover,
|
||||
inlay_hints,
|
||||
};
|
||||
use ty_ide::{NavigationTargets, signature_help};
|
||||
use ty_project::metadata::options::Options;
|
||||
use ty_project::metadata::value::ValueSource;
|
||||
use ty_project::watch::{ChangeEvent, ChangedKind, CreatedKind, DeletedKind};
|
||||
|
|
@ -267,32 +270,61 @@ impl Workspace {
|
|||
return Ok(Vec::new());
|
||||
};
|
||||
|
||||
let source_range = Range::from_text_range(
|
||||
targets.file_range().range(),
|
||||
&index,
|
||||
Ok(map_targets_to_links(
|
||||
&self.db,
|
||||
targets,
|
||||
&source,
|
||||
&index,
|
||||
self.position_encoding,
|
||||
);
|
||||
))
|
||||
}
|
||||
|
||||
let links: Vec<_> = targets
|
||||
.into_iter()
|
||||
.map(|target| LocationLink {
|
||||
path: target.file().path(&self.db).to_string(),
|
||||
full_range: Range::from_file_range(
|
||||
&self.db,
|
||||
FileRange::new(target.file(), target.full_range()),
|
||||
self.position_encoding,
|
||||
),
|
||||
selection_range: Some(Range::from_file_range(
|
||||
&self.db,
|
||||
FileRange::new(target.file(), target.focus_range()),
|
||||
self.position_encoding,
|
||||
)),
|
||||
origin_selection_range: Some(source_range),
|
||||
})
|
||||
.collect();
|
||||
#[wasm_bindgen(js_name = "gotoDeclaration")]
|
||||
pub fn goto_declaration(
|
||||
&self,
|
||||
file_id: &FileHandle,
|
||||
position: Position,
|
||||
) -> Result<Vec<LocationLink>, Error> {
|
||||
let source = source_text(&self.db, file_id.file);
|
||||
let index = line_index(&self.db, file_id.file);
|
||||
|
||||
Ok(links)
|
||||
let offset = position.to_text_size(&source, &index, self.position_encoding)?;
|
||||
|
||||
let Some(targets) = goto_declaration(&self.db, file_id.file, offset) else {
|
||||
return Ok(Vec::new());
|
||||
};
|
||||
|
||||
Ok(map_targets_to_links(
|
||||
&self.db,
|
||||
targets,
|
||||
&source,
|
||||
&index,
|
||||
self.position_encoding,
|
||||
))
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = "gotoDefinition")]
|
||||
pub fn goto_definition(
|
||||
&self,
|
||||
file_id: &FileHandle,
|
||||
position: Position,
|
||||
) -> Result<Vec<LocationLink>, Error> {
|
||||
let source = source_text(&self.db, file_id.file);
|
||||
let index = line_index(&self.db, file_id.file);
|
||||
|
||||
let offset = position.to_text_size(&source, &index, self.position_encoding)?;
|
||||
|
||||
let Some(targets) = goto_definition(&self.db, file_id.file, offset) else {
|
||||
return Ok(Vec::new());
|
||||
};
|
||||
|
||||
Ok(map_targets_to_links(
|
||||
&self.db,
|
||||
targets,
|
||||
&source,
|
||||
&index,
|
||||
self.position_encoding,
|
||||
))
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
|
|
@ -464,6 +496,39 @@ pub(crate) fn into_error<E: std::fmt::Display>(err: E) -> Error {
|
|||
Error::new(&err.to_string())
|
||||
}
|
||||
|
||||
fn map_targets_to_links(
|
||||
db: &dyn Db,
|
||||
targets: RangedValue<NavigationTargets>,
|
||||
source: &SourceText,
|
||||
index: &LineIndex,
|
||||
position_encoding: PositionEncoding,
|
||||
) -> Vec<LocationLink> {
|
||||
let source_range = Range::from_text_range(
|
||||
targets.file_range().range(),
|
||||
index,
|
||||
source,
|
||||
position_encoding,
|
||||
);
|
||||
|
||||
targets
|
||||
.into_iter()
|
||||
.map(|target| LocationLink {
|
||||
path: target.file().path(db).to_string(),
|
||||
full_range: Range::from_file_range(
|
||||
db,
|
||||
FileRange::new(target.file(), target.full_range()),
|
||||
position_encoding,
|
||||
),
|
||||
selection_range: Some(Range::from_file_range(
|
||||
db,
|
||||
FileRange::new(target.file(), target.focus_range()),
|
||||
position_encoding,
|
||||
)),
|
||||
origin_selection_range: Some(source_range),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[wasm_bindgen(inspectable)]
|
||||
pub struct FileHandle {
|
||||
|
|
|
|||
|
|
@ -146,6 +146,8 @@ interface PlaygroundServerProps {
|
|||
class PlaygroundServer
|
||||
implements
|
||||
languages.TypeDefinitionProvider,
|
||||
languages.DeclarationProvider,
|
||||
languages.DefinitionProvider,
|
||||
editor.ICodeEditorOpener,
|
||||
languages.HoverProvider,
|
||||
languages.InlayHintsProvider,
|
||||
|
|
@ -156,6 +158,8 @@ class PlaygroundServer
|
|||
languages.SignatureHelpProvider
|
||||
{
|
||||
private typeDefinitionProviderDisposable: IDisposable;
|
||||
private declarationProviderDisposable: IDisposable;
|
||||
private definitionProviderDisposable: IDisposable;
|
||||
private editorOpenerDisposable: IDisposable;
|
||||
private hoverDisposable: IDisposable;
|
||||
private inlayHintsDisposable: IDisposable;
|
||||
|
|
@ -171,6 +175,10 @@ class PlaygroundServer
|
|||
) {
|
||||
this.typeDefinitionProviderDisposable =
|
||||
monaco.languages.registerTypeDefinitionProvider("python", this);
|
||||
this.declarationProviderDisposable =
|
||||
monaco.languages.registerDeclarationProvider("python", this);
|
||||
this.definitionProviderDisposable =
|
||||
monaco.languages.registerDefinitionProvider("python", this);
|
||||
this.hoverDisposable = monaco.languages.registerHoverProvider(
|
||||
"python",
|
||||
this,
|
||||
|
|
@ -517,29 +525,61 @@ class PlaygroundServer
|
|||
new TyPosition(position.lineNumber, position.column),
|
||||
);
|
||||
|
||||
return (
|
||||
links
|
||||
.map((link) => {
|
||||
const targetSelection =
|
||||
link.selection_range == null
|
||||
? undefined
|
||||
: tyRangeToMonacoRange(link.selection_range);
|
||||
return mapNavigationTargets(links);
|
||||
}
|
||||
|
||||
const originSelection =
|
||||
link.origin_selection_range == null
|
||||
? undefined
|
||||
: tyRangeToMonacoRange(link.origin_selection_range);
|
||||
provideDeclaration(
|
||||
model: editor.ITextModel,
|
||||
position: Position,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_: CancellationToken,
|
||||
): languages.ProviderResult<languages.Definition | languages.LocationLink[]> {
|
||||
const workspace = this.props.workspace;
|
||||
|
||||
return {
|
||||
uri: Uri.parse(link.path),
|
||||
range: tyRangeToMonacoRange(link.full_range),
|
||||
targetSelectionRange: targetSelection,
|
||||
originSelectionRange: originSelection,
|
||||
} as languages.LocationLink;
|
||||
})
|
||||
// Filter out vendored files because they aren't open in the editor.
|
||||
.filter((link) => link.uri.scheme !== "vendored")
|
||||
const selectedFile = this.props.files.selected;
|
||||
if (selectedFile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedHandle = this.props.files.handles[selectedFile];
|
||||
|
||||
if (selectedHandle == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const links = workspace.gotoDeclaration(
|
||||
selectedHandle,
|
||||
new TyPosition(position.lineNumber, position.column),
|
||||
);
|
||||
|
||||
return mapNavigationTargets(links);
|
||||
}
|
||||
|
||||
provideDefinition(
|
||||
model: editor.ITextModel,
|
||||
position: Position,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_: CancellationToken,
|
||||
): languages.ProviderResult<languages.Definition | languages.LocationLink[]> {
|
||||
const workspace = this.props.workspace;
|
||||
|
||||
const selectedFile = this.props.files.selected;
|
||||
if (selectedFile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedHandle = this.props.files.handles[selectedFile];
|
||||
|
||||
if (selectedHandle == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const links = workspace.gotoDefinition(
|
||||
selectedHandle,
|
||||
new TyPosition(position.lineNumber, position.column),
|
||||
);
|
||||
|
||||
return mapNavigationTargets(links);
|
||||
}
|
||||
|
||||
openCodeEditor(
|
||||
|
|
@ -625,6 +665,8 @@ class PlaygroundServer
|
|||
this.hoverDisposable.dispose();
|
||||
this.editorOpenerDisposable.dispose();
|
||||
this.typeDefinitionProviderDisposable.dispose();
|
||||
this.declarationProviderDisposable.dispose();
|
||||
this.definitionProviderDisposable.dispose();
|
||||
this.inlayHintsDisposable.dispose();
|
||||
this.formatDisposable.dispose();
|
||||
this.rangeSemanticTokensDisposable.dispose();
|
||||
|
|
@ -683,6 +725,29 @@ function generateMonacoTokens(
|
|||
return { data: Uint32Array.from(result) };
|
||||
}
|
||||
|
||||
function mapNavigationTargets(links: any[]): languages.LocationLink[] {
|
||||
return links
|
||||
.map((link) => {
|
||||
const targetSelection =
|
||||
link.selection_range == null
|
||||
? undefined
|
||||
: tyRangeToMonacoRange(link.selection_range);
|
||||
|
||||
const originSelection =
|
||||
link.origin_selection_range == null
|
||||
? undefined
|
||||
: tyRangeToMonacoRange(link.origin_selection_range);
|
||||
|
||||
return {
|
||||
uri: Uri.parse(link.path),
|
||||
range: tyRangeToMonacoRange(link.full_range),
|
||||
targetSelectionRange: targetSelection,
|
||||
originSelectionRange: originSelection,
|
||||
} as languages.LocationLink;
|
||||
})
|
||||
.filter((link) => link.uri.scheme !== "vendored");
|
||||
}
|
||||
|
||||
function mapCompletionKind(kind: CompletionKind): CompletionItemKind {
|
||||
switch (kind) {
|
||||
case CompletionKind.Text:
|
||||
|
|
|
|||
Loading…
Reference in New Issue