mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-15 22:54:00 +00:00
implement first pass of memory layout viewer
This commit is contained in:
parent
db0add1ce9
commit
cfa15d49aa
9 changed files with 561 additions and 6 deletions
|
@ -60,6 +60,7 @@ mod interpret_function;
|
||||||
mod view_item_tree;
|
mod view_item_tree;
|
||||||
mod shuffle_crate_graph;
|
mod shuffle_crate_graph;
|
||||||
mod fetch_crates;
|
mod fetch_crates;
|
||||||
|
mod view_memory_layout;
|
||||||
|
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
|
|
||||||
|
@ -74,6 +75,7 @@ use ide_db::{
|
||||||
};
|
};
|
||||||
use syntax::SourceFile;
|
use syntax::SourceFile;
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
use view_memory_layout::{view_memory_layout, RecursiveMemoryLayout};
|
||||||
|
|
||||||
use crate::navigation_target::{ToNav, TryToNav};
|
use crate::navigation_target::{ToNav, TryToNav};
|
||||||
|
|
||||||
|
@ -724,6 +726,10 @@ impl Analysis {
|
||||||
self.with_db(|db| move_item::move_item(db, range, direction))
|
self.with_db(|db| move_item::move_item(db, range, direction))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_recursive_memory_layout(&self, position: FilePosition) -> Cancellable<Option<RecursiveMemoryLayout>> {
|
||||||
|
self.with_db(|db| view_memory_layout(db, position))
|
||||||
|
}
|
||||||
|
|
||||||
/// Performs an operation on the database that may be canceled.
|
/// Performs an operation on the database that may be canceled.
|
||||||
///
|
///
|
||||||
/// rust-analyzer needs to be able to answer semantic questions about the
|
/// rust-analyzer needs to be able to answer semantic questions about the
|
||||||
|
|
170
crates/ide/src/view_memory_layout.rs
Normal file
170
crates/ide/src/view_memory_layout.rs
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
use hir::{Field, HirDisplay, Layout, Semantics, Type};
|
||||||
|
use ide_db::{
|
||||||
|
defs::{Definition, IdentClass},
|
||||||
|
helpers::pick_best_token,
|
||||||
|
RootDatabase,
|
||||||
|
};
|
||||||
|
use syntax::{AstNode, SyntaxKind, SyntaxToken};
|
||||||
|
|
||||||
|
use crate::FilePosition;
|
||||||
|
|
||||||
|
pub struct MemoryLayoutNode {
|
||||||
|
pub item_name: String,
|
||||||
|
pub typename: String,
|
||||||
|
pub size: u64,
|
||||||
|
pub alignment: u64,
|
||||||
|
pub offset: u64,
|
||||||
|
pub parent_idx: i64,
|
||||||
|
pub children_start: i64,
|
||||||
|
pub children_len: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RecursiveMemoryLayout {
|
||||||
|
pub nodes: Vec<MemoryLayoutNode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_definition(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Option<Definition> {
|
||||||
|
for token in sema.descend_into_macros(token) {
|
||||||
|
let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops);
|
||||||
|
if let Some(&[x]) = def.as_deref() {
|
||||||
|
return Some(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn view_memory_layout(
|
||||||
|
db: &RootDatabase,
|
||||||
|
position: FilePosition,
|
||||||
|
) -> Option<RecursiveMemoryLayout> {
|
||||||
|
let sema = Semantics::new(db);
|
||||||
|
let file = sema.parse(position.file_id);
|
||||||
|
let token =
|
||||||
|
pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind {
|
||||||
|
SyntaxKind::IDENT => 3,
|
||||||
|
_ => 0,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let def = get_definition(&sema, token)?;
|
||||||
|
|
||||||
|
let ty = match def {
|
||||||
|
Definition::Adt(it) => it.ty(db),
|
||||||
|
Definition::TypeAlias(it) => it.ty(db),
|
||||||
|
Definition::BuiltinType(it) => it.ty(db),
|
||||||
|
Definition::SelfType(it) => it.self_ty(db),
|
||||||
|
Definition::Local(it) => it.ty(db),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum FieldOrTupleIdx {
|
||||||
|
Field(Field),
|
||||||
|
TupleIdx(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FieldOrTupleIdx {
|
||||||
|
fn name(&self, db: &RootDatabase) -> String {
|
||||||
|
match *self {
|
||||||
|
FieldOrTupleIdx::Field(f) => f
|
||||||
|
.name(db)
|
||||||
|
.as_str()
|
||||||
|
.map(|s| s.to_owned())
|
||||||
|
.unwrap_or_else(|| format!("{:#?}", f.name(db).as_tuple_index().unwrap())),
|
||||||
|
FieldOrTupleIdx::TupleIdx(i) => format!(".{i}").to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn index(&self) -> usize {
|
||||||
|
match *self {
|
||||||
|
FieldOrTupleIdx::Field(f) => f.index(),
|
||||||
|
FieldOrTupleIdx::TupleIdx(i) => i,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_layout(
|
||||||
|
nodes: &mut Vec<MemoryLayoutNode>,
|
||||||
|
db: &RootDatabase,
|
||||||
|
ty: &Type,
|
||||||
|
layout: &Layout,
|
||||||
|
parent_idx: usize,
|
||||||
|
) {
|
||||||
|
let mut fields = ty
|
||||||
|
.fields(db)
|
||||||
|
.into_iter()
|
||||||
|
.map(|(f, ty)| (FieldOrTupleIdx::Field(f), ty))
|
||||||
|
.chain(
|
||||||
|
ty.tuple_fields(db)
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, ty)| (FieldOrTupleIdx::TupleIdx(i), ty)),
|
||||||
|
)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
fields.sort_by_key(|(f, _)| layout.field_offset(f.index()).unwrap_or(u64::MAX));
|
||||||
|
|
||||||
|
if fields.len() == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let children_start = nodes.len();
|
||||||
|
nodes[parent_idx].children_start = children_start as i64;
|
||||||
|
nodes[parent_idx].children_len = fields.len() as u64;
|
||||||
|
|
||||||
|
for (field, child_ty) in fields.iter() {
|
||||||
|
if let Ok(child_layout) = child_ty.layout(db) {
|
||||||
|
nodes.push(MemoryLayoutNode {
|
||||||
|
item_name: field.name(db),
|
||||||
|
typename: child_ty.display(db).to_string(),
|
||||||
|
size: child_layout.size(),
|
||||||
|
alignment: child_layout.align(),
|
||||||
|
offset: layout.field_offset(field.index()).unwrap_or(0),
|
||||||
|
parent_idx: parent_idx as i64,
|
||||||
|
children_start: -1,
|
||||||
|
children_len: 0,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
nodes.push(MemoryLayoutNode {
|
||||||
|
item_name: field.name(db)
|
||||||
|
+ format!("(no layout data: {:?})", child_ty.layout(db).unwrap_err())
|
||||||
|
.as_ref(),
|
||||||
|
typename: child_ty.display(db).to_string(),
|
||||||
|
size: 0,
|
||||||
|
offset: 0,
|
||||||
|
alignment: 0,
|
||||||
|
parent_idx: parent_idx as i64,
|
||||||
|
children_start: -1,
|
||||||
|
children_len: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, (_, child_ty)) in fields.iter().enumerate() {
|
||||||
|
if let Ok(child_layout) = child_ty.layout(db) {
|
||||||
|
read_layout(nodes, db, &child_ty, &child_layout, children_start + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ty.layout(db).map(|layout| {
|
||||||
|
let item_name = match def {
|
||||||
|
Definition::Local(l) => l.name(db).as_str().unwrap().to_owned(),
|
||||||
|
_ => "[ROOT]".to_owned(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let typename = ty.display(db).to_string();
|
||||||
|
|
||||||
|
let mut nodes = vec![MemoryLayoutNode {
|
||||||
|
item_name,
|
||||||
|
typename: typename.clone(),
|
||||||
|
size: layout.size(),
|
||||||
|
offset: 0,
|
||||||
|
alignment: layout.align(),
|
||||||
|
parent_idx: -1,
|
||||||
|
children_start: -1,
|
||||||
|
children_len: 0,
|
||||||
|
}];
|
||||||
|
read_layout(&mut nodes, db, &ty, &layout, 0);
|
||||||
|
|
||||||
|
RecursiveMemoryLayout { nodes }
|
||||||
|
})
|
||||||
|
}
|
|
@ -1689,6 +1689,34 @@ pub(crate) fn handle_move_item(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn handle_view_recursive_memory_layout(
|
||||||
|
snap: GlobalStateSnapshot,
|
||||||
|
params: lsp_ext::ViewRecursiveMemoryLayoutParams,
|
||||||
|
) -> Result<Option<lsp_ext::RecursiveMemoryLayout>> {
|
||||||
|
let _p = profile::span("view_recursive_memory_layout");
|
||||||
|
let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?;
|
||||||
|
let line_index = snap.file_line_index(file_id)?;
|
||||||
|
let offset = from_proto::offset(&line_index, params.position)?;
|
||||||
|
|
||||||
|
let res = snap.analysis.get_recursive_memory_layout(FilePosition { file_id, offset })?;
|
||||||
|
Ok(res.map(|it| lsp_ext::RecursiveMemoryLayout {
|
||||||
|
nodes: it
|
||||||
|
.nodes
|
||||||
|
.iter()
|
||||||
|
.map(|n| lsp_ext::MemoryLayoutNode {
|
||||||
|
item_name: n.item_name.clone(),
|
||||||
|
typename: n.typename.clone(),
|
||||||
|
size: n.size,
|
||||||
|
offset: n.offset,
|
||||||
|
alignment: n.alignment,
|
||||||
|
parent_idx: n.parent_idx,
|
||||||
|
children_start: n.children_start,
|
||||||
|
children_len: n.children_len,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
fn to_command_link(command: lsp_types::Command, tooltip: String) -> lsp_ext::CommandLink {
|
fn to_command_link(command: lsp_types::Command, tooltip: String) -> lsp_ext::CommandLink {
|
||||||
lsp_ext::CommandLink { tooltip: Some(tooltip), command }
|
lsp_ext::CommandLink { tooltip: Some(tooltip), command }
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,6 +182,40 @@ pub struct ExpandedMacro {
|
||||||
pub expansion: String,
|
pub expansion: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum ViewRecursiveMemoryLayout {}
|
||||||
|
|
||||||
|
impl Request for ViewRecursiveMemoryLayout {
|
||||||
|
type Params = ViewRecursiveMemoryLayoutParams;
|
||||||
|
type Result = Option<RecursiveMemoryLayout>;
|
||||||
|
const METHOD: &'static str = "rust-analyzer/viewRecursiveMemoryLayout";
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct ViewRecursiveMemoryLayoutParams {
|
||||||
|
pub text_document: TextDocumentIdentifier,
|
||||||
|
pub position: Position,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RecursiveMemoryLayout {
|
||||||
|
pub nodes: Vec<MemoryLayoutNode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct MemoryLayoutNode {
|
||||||
|
pub item_name: String,
|
||||||
|
pub typename: String,
|
||||||
|
pub size: u64,
|
||||||
|
pub offset: u64,
|
||||||
|
pub alignment: u64,
|
||||||
|
pub parent_idx: i64,
|
||||||
|
pub children_start: i64,
|
||||||
|
pub children_len: u64,
|
||||||
|
}
|
||||||
|
|
||||||
pub enum CancelFlycheck {}
|
pub enum CancelFlycheck {}
|
||||||
|
|
||||||
impl Notification for CancelFlycheck {
|
impl Notification for CancelFlycheck {
|
||||||
|
|
|
@ -753,6 +753,7 @@ impl GlobalState {
|
||||||
)
|
)
|
||||||
.on::<lsp_types::request::WillRenameFiles>(handlers::handle_will_rename_files)
|
.on::<lsp_types::request::WillRenameFiles>(handlers::handle_will_rename_files)
|
||||||
.on::<lsp_ext::Ssr>(handlers::handle_ssr)
|
.on::<lsp_ext::Ssr>(handlers::handle_ssr)
|
||||||
|
.on::<lsp_ext::ViewRecursiveMemoryLayout>(handlers::handle_view_recursive_memory_layout)
|
||||||
.finish();
|
.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -284,6 +284,11 @@
|
||||||
"command": "rust-analyzer.revealDependency",
|
"command": "rust-analyzer.revealDependency",
|
||||||
"title": "Reveal File",
|
"title": "Reveal File",
|
||||||
"category": "rust-analyzer"
|
"category": "rust-analyzer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "rust-analyzer.viewMemoryLayout",
|
||||||
|
"title": "View Memory Layout",
|
||||||
|
"category": "rust-analyzer"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"keybindings": [
|
"keybindings": [
|
||||||
|
@ -2067,6 +2072,10 @@
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.openCargoToml",
|
"command": "rust-analyzer.openCargoToml",
|
||||||
"when": "inRustProject"
|
"when": "inRustProject"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "rust-analyzer.viewMemoryLayout",
|
||||||
|
"when": "inRustProject"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"editor/context": [
|
"editor/context": [
|
||||||
|
|
|
@ -1129,3 +1129,286 @@ export function linkToCommand(_: Ctx): Cmd {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function viewMemoryLayout(ctx: CtxInit): Cmd {
|
||||||
|
return async () => {
|
||||||
|
|
||||||
|
const editor = vscode.window.activeTextEditor;
|
||||||
|
if (!editor) return "";
|
||||||
|
const client = ctx.client;
|
||||||
|
|
||||||
|
const position = editor.selection.active;
|
||||||
|
const expanded = await client.sendRequest(ra.viewRecursiveMemoryLayout, {
|
||||||
|
textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(
|
||||||
|
editor.document
|
||||||
|
),
|
||||||
|
position,
|
||||||
|
});
|
||||||
|
|
||||||
|
// if (expanded == null) return "Not available";
|
||||||
|
|
||||||
|
|
||||||
|
const document = vscode.window.createWebviewPanel(
|
||||||
|
"memory_layout",
|
||||||
|
"[Memory Layout]",
|
||||||
|
vscode.ViewColumn.Two,
|
||||||
|
{ enableScripts: true, });
|
||||||
|
|
||||||
|
document.webview.html = `<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Document</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
padding: 32px;
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
background-color: var(--vscode-editor-background);
|
||||||
|
font-family: var(--vscode-editor-font-family);
|
||||||
|
font-size: var(--vscode-editor-font-size);
|
||||||
|
color: var(--vscode-editor-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trans {
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
color: var(--vscode-commandCenter-activeBorder);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-line {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
background-color: var(--vscode-commandCenter-activeBorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
#tooltip {
|
||||||
|
position: fixed;
|
||||||
|
display: none;
|
||||||
|
z-index: 1;
|
||||||
|
pointer-events: none;
|
||||||
|
padding: 4px 8px;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
|
color: var(--vscode-editorHoverWidget-foreground);
|
||||||
|
background-color: var(--vscode-editorHoverWidget-background);
|
||||||
|
border: 1px solid var(--vscode-editorHoverWidget-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
#tooltip b {
|
||||||
|
color: var(--vscode-editorInlayHint-typeForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
#tooltip ul {
|
||||||
|
margin-left: 0;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
position: absolute;
|
||||||
|
transform: rotateZ(90deg) rotateX(180deg);
|
||||||
|
transform-origin: top left;
|
||||||
|
border-collapse: collapse;
|
||||||
|
table-layout: fixed;
|
||||||
|
left: 48px;
|
||||||
|
top: 0;
|
||||||
|
max-height: calc(100vw - 64px - 48px);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
border: 1px solid var(--vscode-focusBorder);
|
||||||
|
writing-mode: vertical-rl;
|
||||||
|
text-orientation: sideways-right;
|
||||||
|
|
||||||
|
height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
td p {
|
||||||
|
height: calc(100% - 16px);
|
||||||
|
width: calc(100% - 8px);
|
||||||
|
margin: 8px 4px;
|
||||||
|
display: inline-block;
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
pointer-events: none;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
td p * {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: inline-block;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
td p b {
|
||||||
|
color: var(--vscode-editorInlayHint-typeForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
td:hover {
|
||||||
|
background-color: var(--vscode-editor-hoverHighlightBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
td:empty {
|
||||||
|
visibility: hidden;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="tooltip"></div>
|
||||||
|
</body>
|
||||||
|
<script>(function() {
|
||||||
|
|
||||||
|
const data = ${JSON.stringify(expanded)}
|
||||||
|
|
||||||
|
if (!(data && data.nodes.length)) {
|
||||||
|
document.body.innerText = "Not Available"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data.nodes.map(n => {
|
||||||
|
n.typename = n.typename.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', ' & quot; ').replaceAll("'", ''')
|
||||||
|
return n
|
||||||
|
})
|
||||||
|
|
||||||
|
let height = window.innerHeight - 64
|
||||||
|
|
||||||
|
addEventListener("resize", e => {
|
||||||
|
const new_height = window.innerHeight - 64
|
||||||
|
height = new_height
|
||||||
|
container.classList.remove("trans")
|
||||||
|
table.classList.remove("trans")
|
||||||
|
locate()
|
||||||
|
setTimeout(() => { // give delay to redraw, annoying but needed
|
||||||
|
container.classList.add("trans")
|
||||||
|
table.classList.add("trans")
|
||||||
|
}, 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
const container = document.createElement("div")
|
||||||
|
container.classList.add("container")
|
||||||
|
container.classList.add("trans")
|
||||||
|
document.body.appendChild(container)
|
||||||
|
|
||||||
|
const tooltip = document.getElementById("tooltip")
|
||||||
|
|
||||||
|
let y = 0
|
||||||
|
let zoom = 1.0
|
||||||
|
|
||||||
|
const table = document.createElement("table")
|
||||||
|
table.classList.add("trans")
|
||||||
|
container.appendChild(table)
|
||||||
|
const rows = []
|
||||||
|
|
||||||
|
function node_t(idx, depth, offset) {
|
||||||
|
if (!rows[depth]) {
|
||||||
|
rows[depth] = { el: document.createElement("tr"), offset: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rows[depth].offset < offset) {
|
||||||
|
const pad = document.createElement("td")
|
||||||
|
pad.colSpan = offset - rows[depth].offset
|
||||||
|
rows[depth].el.appendChild(pad)
|
||||||
|
rows[depth].offset += offset - rows[depth].offset
|
||||||
|
}
|
||||||
|
|
||||||
|
const td = document.createElement("td")
|
||||||
|
td.innerHTML = '<p><span>' + data.nodes[idx].itemName + ':</span> <b>' + data.nodes[idx].typename + '</b></p>'
|
||||||
|
|
||||||
|
td.colSpan = data.nodes[idx].size
|
||||||
|
|
||||||
|
td.addEventListener("mouseover", e => {
|
||||||
|
const node = data.nodes[idx]
|
||||||
|
tooltip.innerHTML = node.itemName + ": <b>" + node.typename + "</b><br/>"
|
||||||
|
+ "<ul>"
|
||||||
|
+ "<li>size = " + node.size + "</li>"
|
||||||
|
+ "<li>align = " + node.alignment + "</li>"
|
||||||
|
+ "<li>field offset = " + node.offset + "</li>"
|
||||||
|
+ "</ul>"
|
||||||
|
+ "<i>double click to focus</i>"
|
||||||
|
|
||||||
|
tooltip.style.display = "block"
|
||||||
|
})
|
||||||
|
td.addEventListener("mouseleave", _ => tooltip.style.display = "none")
|
||||||
|
const total_offset = rows[depth].offset
|
||||||
|
td.addEventListener("dblclick", e => {
|
||||||
|
const node = data.nodes[idx]
|
||||||
|
zoom = data.nodes[0].size / node.size
|
||||||
|
y = -(total_offset) / data.nodes[0].size * zoom
|
||||||
|
x = 0
|
||||||
|
locate()
|
||||||
|
})
|
||||||
|
|
||||||
|
rows[depth].el.appendChild(td)
|
||||||
|
rows[depth].offset += data.nodes[idx].size
|
||||||
|
|
||||||
|
|
||||||
|
if (data.nodes[idx].childrenStart != -1) {
|
||||||
|
for (let i = 0; i < data.nodes[idx].childrenLen; i++) {
|
||||||
|
if (data.nodes[data.nodes[idx].childrenStart + i].size) {
|
||||||
|
node_t(data.nodes[idx].childrenStart + i, depth + 1, offset + data.nodes[data.nodes[idx].childrenStart + i].offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node_t(0, 0, 0)
|
||||||
|
|
||||||
|
for (const row of rows) table.appendChild(row.el)
|
||||||
|
|
||||||
|
const grid = document.createElement("div")
|
||||||
|
grid.classList.add("grid")
|
||||||
|
container.appendChild(grid)
|
||||||
|
|
||||||
|
for (let i = 0; i < data.nodes[0].size / 8 + 1; i++) {
|
||||||
|
const el = document.createElement("div")
|
||||||
|
el.classList.add("grid-line")
|
||||||
|
el.style.top = (i / (data.nodes[0].size / 8) * 100) + "%"
|
||||||
|
el.innerText = i * 8
|
||||||
|
grid.appendChild(el)
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventListener("mousemove", e => {
|
||||||
|
tooltip.style.top = e.clientY + 10 + "px"
|
||||||
|
tooltip.style.left = e.clientX + 10 + "px"
|
||||||
|
})
|
||||||
|
|
||||||
|
function locate() {
|
||||||
|
container.style.top = height * y + "px"
|
||||||
|
container.style.height = (height * zoom) + "px"
|
||||||
|
|
||||||
|
table.style.width = container.style.height
|
||||||
|
}
|
||||||
|
|
||||||
|
locate()
|
||||||
|
|
||||||
|
})()
|
||||||
|
</script>
|
||||||
|
</html>`
|
||||||
|
|
||||||
|
ctx.pushExtCleanup(document);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ export const viewItemTree = new lc.RequestType<ViewItemTreeParams, string, void>
|
||||||
|
|
||||||
export type AnalyzerStatusParams = { textDocument?: lc.TextDocumentIdentifier };
|
export type AnalyzerStatusParams = { textDocument?: lc.TextDocumentIdentifier };
|
||||||
|
|
||||||
export interface FetchDependencyListParams {}
|
export interface FetchDependencyListParams { }
|
||||||
|
|
||||||
export interface FetchDependencyListResult {
|
export interface FetchDependencyListResult {
|
||||||
crates: {
|
crates: {
|
||||||
|
@ -86,7 +86,7 @@ export const fetchDependencyList = new lc.RequestType<
|
||||||
void
|
void
|
||||||
>("rust-analyzer/fetchDependencyList");
|
>("rust-analyzer/fetchDependencyList");
|
||||||
|
|
||||||
export interface FetchDependencyGraphParams {}
|
export interface FetchDependencyGraphParams { }
|
||||||
|
|
||||||
export interface FetchDependencyGraphResult {
|
export interface FetchDependencyGraphResult {
|
||||||
crates: {
|
crates: {
|
||||||
|
@ -150,6 +150,9 @@ export const serverStatus = new lc.NotificationType<ServerStatusParams>(
|
||||||
"experimental/serverStatus"
|
"experimental/serverStatus"
|
||||||
);
|
);
|
||||||
export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>("experimental/ssr");
|
export const ssr = new lc.RequestType<SsrParams, lc.WorkspaceEdit, void>("experimental/ssr");
|
||||||
|
export const viewRecursiveMemoryLayout = new lc.RequestType<ViewRecursiveMemoryLayoutParams, RecursiveMemoryLayout | null, void>(
|
||||||
|
"rust-analyzer/viewRecursiveMemoryLayout"
|
||||||
|
);
|
||||||
|
|
||||||
export type JoinLinesParams = {
|
export type JoinLinesParams = {
|
||||||
textDocument: lc.TextDocumentIdentifier;
|
textDocument: lc.TextDocumentIdentifier;
|
||||||
|
@ -197,3 +200,23 @@ export type SsrParams = {
|
||||||
position: lc.Position;
|
position: lc.Position;
|
||||||
selections: readonly lc.Range[];
|
selections: readonly lc.Range[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ViewRecursiveMemoryLayoutParams = {
|
||||||
|
textDocument: lc.TextDocumentIdentifier;
|
||||||
|
position: lc.Position;
|
||||||
|
};
|
||||||
|
export type RecursiveMemoryLayoutNode = {
|
||||||
|
item_name: string;
|
||||||
|
typename: string;
|
||||||
|
size: number;
|
||||||
|
alignment: number;
|
||||||
|
offset: number;
|
||||||
|
parent_idx: number;
|
||||||
|
children_start: number;
|
||||||
|
children_len: number;
|
||||||
|
};
|
||||||
|
export type RecursiveMemoryLayout = {
|
||||||
|
name: string;
|
||||||
|
expansion: string;
|
||||||
|
nodes: RecursiveMemoryLayoutNode[];
|
||||||
|
};
|
|
@ -28,7 +28,7 @@ export async function activate(
|
||||||
"both plugins to not work correctly. You should disable one of them.",
|
"both plugins to not work correctly. You should disable one of them.",
|
||||||
"Got it"
|
"Got it"
|
||||||
)
|
)
|
||||||
.then(() => {}, console.error);
|
.then(() => { }, console.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ctx = new Ctx(context, createCommands(), fetchWorkspace());
|
const ctx = new Ctx(context, createCommands(), fetchWorkspace());
|
||||||
|
@ -144,7 +144,7 @@ function createCommands(): Record<string, CommandFactory> {
|
||||||
health: "stopped",
|
health: "stopped",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
disabled: (_) => async () => {},
|
disabled: (_) => async () => { },
|
||||||
},
|
},
|
||||||
|
|
||||||
analyzerStatus: { enabled: commands.analyzerStatus },
|
analyzerStatus: { enabled: commands.analyzerStatus },
|
||||||
|
@ -179,6 +179,7 @@ function createCommands(): Record<string, CommandFactory> {
|
||||||
runFlycheck: { enabled: commands.runFlycheck },
|
runFlycheck: { enabled: commands.runFlycheck },
|
||||||
ssr: { enabled: commands.ssr },
|
ssr: { enabled: commands.ssr },
|
||||||
serverVersion: { enabled: commands.serverVersion },
|
serverVersion: { enabled: commands.serverVersion },
|
||||||
|
viewMemoryLayout: { enabled: commands.viewMemoryLayout },
|
||||||
// Internal commands which are invoked by the server.
|
// Internal commands which are invoked by the server.
|
||||||
applyActionGroup: { enabled: commands.applyActionGroup },
|
applyActionGroup: { enabled: commands.applyActionGroup },
|
||||||
applySnippetWorkspaceEdit: { enabled: commands.applySnippetWorkspaceEditCommand },
|
applySnippetWorkspaceEdit: { enabled: commands.applySnippetWorkspaceEditCommand },
|
||||||
|
|
Loading…
Reference in a new issue