Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
50 changes: 21 additions & 29 deletions crates/engine/src/exec/dap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ use crate::{
inline_frames_for_operation, resolve_typed_variable_values, resolve_variable_values,
},
exec::state::CurrentCycleInfo,
normalize_source_path,
};

// DAP CONFIG
Expand Down Expand Up @@ -650,35 +651,6 @@ fn is_internal_procedure(context_name: &str) -> bool {
context_name.contains("::intrinsics::")
}

fn normalize_source_path(path: &str) -> String {
let path = path.trim();
let path = path.strip_prefix("file://").unwrap_or(path);
let path = path.replace('\\', "/");

let is_absolute = path.starts_with('/');
let mut parts = Vec::new();
for part in path.split('/') {
match part {
"" | "." => {}
".." => {
if parts.last().is_some_and(|last| *last != "..") {
parts.pop();
} else {
parts.push(part);
}
}
_ => parts.push(part),
}
}

let normalized = parts.join("/");
if is_absolute && !normalized.is_empty() {
format!("/{normalized}")
} else {
normalized
}
}

fn strip_source_prefix(path: &str, prefix: &str) -> Option<String> {
let path = path.trim_start_matches('/');
let prefix = prefix.trim_start_matches('/').trim_end_matches('/');
Expand Down Expand Up @@ -2831,6 +2803,26 @@ mod tests {
"/workspace/compiler/examples/fibonacci/src/lib.rs",
&[],
));
assert!(source_paths_match(
"file:///C:/workspace/compiler/examples/fibonacci/src/lib.rs",
"C:/workspace/compiler/examples/fibonacci/src/lib.rs",
&[],
));
assert!(source_paths_match(
"file:///C:/workspace/compiler/examples/fibonacci/src/lib.rs",
"c:/workspace/compiler/examples/fibonacci/src/lib.rs",
&[],
));
assert!(source_paths_match(
"file://localhost/C:/workspace/compiler/examples/fibonacci/src/lib.rs",
"c:/workspace/compiler/examples/fibonacci/src/lib.rs",
&[],
));
assert!(source_paths_match(
"file:///C:/workspace/compiler/examples/fibonacci/src/lib.rs",
"src/lib.rs",
&["c:/workspace/compiler/examples/fibonacci".into()],
));
assert!(!source_paths_match(
"/workspace/compiler/examples/fibonacci/src/lib.rs",
"src/lib.rs",
Expand Down
2 changes: 2 additions & 0 deletions crates/engine/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod felt;
mod linker;
pub mod profiling;
mod registry;
mod source_path;
#[cfg(test)]
mod test_utils;

Expand All @@ -17,4 +18,5 @@ pub use self::{
felt::{Felt, FromMidenRepr, ToMidenRepr, bytes_to_words, push_wasm_ty_to_operand_stack},
linker::LinkLibrary,
registry::HybridPackageRegistry,
source_path::normalize_source_path,
};
78 changes: 78 additions & 0 deletions crates/engine/src/source_path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use miden_debug_types::Uri;

/// Converts a source URI or path into a stable form for loading and comparison.
pub fn normalize_source_path(path: &str) -> String {
let path = path.trim();
let path = Uri::new(path)
.to_path()
.map(|path| path.to_string_lossy().into_owned())
.unwrap_or_else(|| path.to_owned());
let mut path = path.replace('\\', "/");
if path
.as_bytes()
.get(0..2)
.is_some_and(|bytes| bytes[0].is_ascii_alphabetic() && bytes[1] == b':')
{
path.replace_range(..1, &path[..1].to_ascii_lowercase());
}

let is_absolute = path.starts_with('/');
let mut parts = Vec::new();
for part in path.split('/') {
match part {
"" | "." => {}
".." => {
if parts.last().is_some_and(|last| *last != "..") {
parts.pop();
} else {
parts.push(part);
}
}
_ => parts.push(part),
}
}

let normalized = parts.join("/");
if is_absolute && !normalized.is_empty() {
format!("/{normalized}")
} else {
normalized
}
}

#[cfg(test)]
mod tests {
use super::normalize_source_path;

#[test]
fn normalizes_file_uris_and_windows_drive_letters() {
assert_eq!(
normalize_source_path("file:///C:/Users/me/program.masm"),
"c:/Users/me/program.masm"
);
assert_eq!(
normalize_source_path("file:///c:/Users/me/program.masm"),
"c:/Users/me/program.masm"
);
assert_eq!(
normalize_source_path("file://localhost/C:/Users/me/program.masm"),
"c:/Users/me/program.masm"
);
assert_eq!(
normalize_source_path("C:\\Users\\me\\program.masm"),
"c:/Users/me/program.masm"
);
}

#[test]
fn normalizes_path_components() {
assert_eq!(
normalize_source_path("/home/me/./src/../program.masm"),
"/home/me/program.masm"
);
assert_eq!(
normalize_source_path("relative/./src/../program.masm"),
"relative/program.masm"
);
}
}
10 changes: 3 additions & 7 deletions src/dap_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{path::Path, sync::Arc};
use miden_assembly::{Assembler, DefaultSourceManager, SourceManager};
use miden_assembly_syntax::diagnostics::{IntoDiagnostic, Report};
use miden_core::{Word, events::EventId};
use miden_debug_engine::HybridPackageRegistry;
use miden_debug_engine::{HybridPackageRegistry, normalize_source_path};
use miden_debug_types::{Location, SourceFile, SourceManagerExt, SourceSpan};
use miden_mast_package::{Package, PackageId};
use miden_package_registry::{PackageProvider, PackageRegistry};
Expand Down Expand Up @@ -77,12 +77,8 @@ impl StandaloneDapHost {
return Some(file);
}

let path = location
.uri()
.as_str()
.strip_prefix("file://")
.unwrap_or_else(|| location.uri().as_str());
self.source_manager.load_file(Path::new(path)).ok()
let path = normalize_source_path(location.uri().as_str());
self.source_manager.load_file(Path::new(&path)).ok()
}
}

Expand Down
31 changes: 1 addition & 30 deletions src/ui/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{

use miden_assembly::{DefaultSourceManager, SourceManager};
use miden_assembly_syntax::diagnostics::Report;
use miden_debug_engine::DebugQuery;
use miden_debug_engine::{DebugQuery, normalize_source_path};
use miden_debug_types::{Location, SourceManagerExt, SourceSpan};
use miden_mast_package::Package;
use miden_processor::{
Expand Down Expand Up @@ -1279,35 +1279,6 @@ fn source_var_location_is_visible(
source_paths_match(var_path, current_path, source_path_prefixes) && var_line < current_line
}

fn normalize_source_path(path: &str) -> String {
let path = path.trim();
let path = path.strip_prefix("file://").unwrap_or(path);
let path = path.replace('\\', "/");

let is_absolute = path.starts_with('/');
let mut parts = Vec::new();
for part in path.split('/') {
match part {
"" | "." => {}
".." => {
if parts.last().is_some_and(|last| *last != "..") {
parts.pop();
} else {
parts.push(part);
}
}
_ => parts.push(part),
}
}

let normalized = parts.join("/");
if is_absolute && !normalized.is_empty() {
format!("/{normalized}")
} else {
normalized
}
}

fn strip_source_prefix(path: &str, prefix: &str) -> Option<String> {
let path = path.trim_start_matches('/');
let prefix = prefix.trim_start_matches('/').trim_end_matches('/');
Expand Down