Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move the Location struct to a more common place #1106

Merged
merged 1 commit into from
Feb 7, 2024
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
1 change: 1 addition & 0 deletions compiler/qsc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod compile;
pub mod error;
pub mod incremental;
pub mod interpret;
pub mod location;
pub mod target;

pub use qsc_frontend::compile::{
Expand Down
256 changes: 256 additions & 0 deletions compiler/qsc/src/location.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use std::sync::Arc;

use qsc_data_structures::{
line_column::{Encoding, Range},
span::Span,
};
use qsc_frontend::compile::PackageStore;
use qsc_hir::hir::PackageId;

pub const QSHARP_LIBRARY_URI_SCHEME: &str = "qsharp-library-source";

/// Describes a location in source code in terms of a source name and [`Range`].
#[derive(Debug, PartialEq, Clone)]
pub struct Location {
pub source: Arc<str>,
pub range: Range,
}

impl Location {
/// Creates a [`Location`] from a package ID and a SourceMap-relative span.
///
/// To differentiate user sources from library sources, this function takes
/// a `user_package_id` parameter which denotes the user package.
/// All other packages in the package store are assumed to be library packages.
/// Source names from library packages are prepended with a unique URI scheme.
#[must_use]
pub fn from(
span: Span,
package_id: PackageId,
package_store: &PackageStore,
user_package_id: PackageId,
position_encoding: Encoding,
) -> Self {
let source = package_store
.get(package_id)
.expect("package id must exist in store")
.sources
.find_by_offset(span.lo)
.expect("source should exist for offset");

let source_name = if package_id == user_package_id {
source.name.clone()
} else {
// Currently the only supported external packages are our library packages,
// URI's to which need to include our custom library scheme.
format!("{}:{}", QSHARP_LIBRARY_URI_SCHEME, source.name).into()
};

Location {
source: source_name,
range: Range::from_span(position_encoding, &source.contents, &(span - source.offset)),
}
}
}

#[cfg(test)]
mod tests {
use crate::compile;
use expect_test::expect;
use qsc_data_structures::{line_column::Encoding, span::Span};
use qsc_frontend::compile::{PackageStore, RuntimeCapabilityFlags, SourceMap};
use qsc_hir::hir::PackageId;
use qsc_passes::PackageType;

use super::Location;

#[test]
fn from_std_span() {
let (store, std_package_id, user_package_id) = compile_package();

let location = Location::from(
Span { lo: 0, hi: 1 },
std_package_id,
&store,
user_package_id,
Encoding::Utf8,
);

expect![[r#"
Location {
source: "qsharp-library-source:arrays.qs",
range: Range {
start: Position {
line: 0,
column: 0,
},
end: Position {
line: 0,
column: 1,
},
},
}
"#]]
.assert_debug_eq(&location);
}

#[test]
fn from_core_span() {
let (store, _, user_package_id) = compile_package();

let location = Location::from(
Span { lo: 0, hi: 1 },
PackageId::CORE,
&store,
user_package_id,
Encoding::Utf8,
);

expect![[r#"
Location {
source: "qsharp-library-source:core/core.qs",
range: Range {
start: Position {
line: 0,
column: 0,
},
end: Position {
line: 0,
column: 1,
},
},
}
"#]]
.assert_debug_eq(&location);
}

#[test]
fn from_user_span() {
let (store, _, user_package_id) = compile_package();

let bar_start_offset = store
.get(user_package_id)
.expect("expected to find user package")
.sources
.find_by_name("bar.qs")
.expect("expected to find source")
.offset;

let location = Location::from(
Span {
lo: bar_start_offset,
hi: bar_start_offset + 1,
},
user_package_id,
&store,
user_package_id,
Encoding::Utf8,
);

expect![[r#"
Location {
source: "bar.qs",
range: Range {
start: Position {
line: 0,
column: 0,
},
end: Position {
line: 0,
column: 1,
},
},
}
"#]]
.assert_debug_eq(&location);
}

#[test]
fn from_out_of_bounds_lo() {
let (store, _, user_package_id) = compile_package();

let location = Location::from(
Span { lo: 1000, hi: 2000 },
user_package_id,
&store,
user_package_id,
Encoding::Utf8,
);

// Per [`Range`] spec, out of bounds positions map to EOF
expect![[r#"
Location {
source: "bar.qs",
range: Range {
start: Position {
line: 0,
column: 17,
},
end: Position {
line: 0,
column: 17,
},
},
}
"#]]
.assert_debug_eq(&location);
}

#[test]
fn from_out_of_bounds_hi() {
let (store, _, user_package_id) = compile_package();

let location = Location::from(
Span { lo: 0, hi: 2000 },
user_package_id,
&store,
user_package_id,
Encoding::Utf8,
);

// Per [`Range`] spec, out of bounds positions map to EOF
expect![[r#"
Location {
source: "foo.qs",
range: Range {
start: Position {
line: 0,
column: 0,
},
end: Position {
line: 0,
column: 17,
},
},
}
"#]]
.assert_debug_eq(&location);
}

fn compile_package() -> (PackageStore, PackageId, PackageId) {
let mut store = PackageStore::new(compile::core());
let mut dependencies = Vec::new();

let (package_type, capabilities) = (PackageType::Lib, RuntimeCapabilityFlags::all());

let std = compile::std(&store, capabilities);
let std_package_id = store.insert(std);

dependencies.push(std_package_id);
let sources = SourceMap::new(
[
("foo.qs".into(), "namespace Foo { }".into()),
("bar.qs".into(), "namespace Bar { }".into()),
],
None,
);
let (unit, _) =
compile::compile(&store, &dependencies, sources, package_type, capabilities);
let user_package_id = store.insert(unit);

(store, std_package_id, user_package_id)
}
}
2 changes: 1 addition & 1 deletion language_service/src/definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ mod tests;

use crate::compilation::Compilation;
use crate::name_locator::{Handler, Locator, LocatorContext};
use crate::protocol::Location;
use crate::qsc_utils::into_location;
use qsc::ast::visit::Visitor;
use qsc::hir::PackageId;
use qsc::line_column::{Encoding, Position};
use qsc::location::Location;
use qsc::{ast, hir, Span};

pub(crate) fn get_definition(
Expand Down
17 changes: 7 additions & 10 deletions language_service/src/definition/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
#![allow(clippy::needless_raw_string_hashes)]

use expect_test::{expect, Expect};
use qsc::location::Location;

use super::get_definition;
use crate::{
protocol::Location,
test_utils::{
compile_notebook_with_fake_stdlib_and_markers, compile_with_fake_stdlib_and_markers,
},
Expand All @@ -26,8 +26,8 @@ fn assert_definition(source_with_markers: &str) {
None
} else {
Some(Location {
source: "<source>".to_string(),
span: target_spans[0],
source: "<source>".into(),
range: target_spans[0],
})
};
assert_eq!(&expected_definition, &actual_definition);
Expand All @@ -40,10 +40,7 @@ fn assert_definition_notebook(cells_with_markers: &[(&str, &str)]) {
let expected_definition = if target_spans.is_empty() {
None
} else {
Some(Location {
source: target_spans[0].0.clone(),
span: target_spans[0].1,
})
Some(target_spans[0].clone())
};
assert_eq!(&expected_definition, &actual_definition);
}
Expand Down Expand Up @@ -302,7 +299,7 @@ fn std_call() {
Some(
Location {
source: "qsharp-library-source:<std>",
span: Range {
range: Range {
start: Position {
line: 1,
column: 26,
Expand Down Expand Up @@ -410,7 +407,7 @@ fn std_udt() {
Some(
Location {
source: "qsharp-library-source:<std>",
span: Range {
range: Range {
start: Position {
line: 4,
column: 24,
Expand Down Expand Up @@ -442,7 +439,7 @@ fn std_udt_udt_field() {
Some(
Location {
source: "qsharp-library-source:<std>",
span: Range {
range: Range {
start: Position {
line: 4,
column: 31,
Expand Down
2 changes: 1 addition & 1 deletion language_service/src/hover/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ fn check_notebook(cells_with_markers: &[(&str, &str)], expect: &Expect) {

let actual =
get_hover(&compilation, &cell_uri, position, Encoding::Utf8).expect("Expected a hover.");
assert_eq!(&actual.span, &target_spans[0].1);
assert_eq!(&actual.span, &target_spans[0].range);
expect.assert_eq(&actual.contents);
}

Expand Down
7 changes: 5 additions & 2 deletions language_service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@ use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
use futures_util::StreamExt;
use log::{trace, warn};
use protocol::{
CompletionList, DiagnosticUpdate, Hover, Location, NotebookMetadata, SignatureHelp,
CompletionList, DiagnosticUpdate, Hover, NotebookMetadata, SignatureHelp,
WorkspaceConfigurationUpdate,
};
use qsc::line_column::{Encoding, Position, Range};
use qsc::{
line_column::{Encoding, Position, Range},
location::Location,
};
use qsc_project::JSFileEntry;
use state::{CompilationState, CompilationStateUpdater};
use std::{cell::RefCell, fmt::Debug, future::Future, pin::Pin, rc::Rc, sync::Arc};
Expand Down
6 changes: 0 additions & 6 deletions language_service/src/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,6 @@ impl Hash for CompletionItem {
}
}

#[derive(Debug, PartialEq)]
pub struct Location {
pub source: String,
pub span: Range,
}

#[derive(Debug, PartialEq)]
pub struct Hover {
pub contents: String,
Expand Down
Loading
Loading