summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2022-05-15 14:41:37 -0400
committerGitHub <noreply@github.com>2022-05-15 14:41:37 -0400
commiteb5ffab1cbc010424aa1764005f71dcd67525dc1 (patch)
tree31f66727bb590a99f4e67a11e4874f72f9d3567e
parent8744ee883e093ecc09add4054177f8afa793ec77 (diff)
fix(lsp): correct positions in some scenarios (#14359)
-rw-r--r--cli/build.rs13
-rw-r--r--cli/lsp/documents.rs7
-rw-r--r--cli/lsp/text.rs261
-rw-r--r--cli/lsp/tsc.rs236
-rw-r--r--cli/tests/integration/lsp_tests.rs12
-rw-r--r--cli/tsc.rs14
-rw-r--r--cli/tsc/99_main_compiler.js86
7 files changed, 96 insertions, 533 deletions
diff --git a/cli/build.rs b/cli/build.rs
index 019f8d02d..e7a2da236 100644
--- a/cli/build.rs
+++ b/cli/build.rs
@@ -198,6 +198,14 @@ fn create_compiler_snapshot(
}
#[op]
+ fn op_script_version(
+ _state: &mut OpState,
+ _args: Value,
+ ) -> Result<Option<String>, AnyError> {
+ Ok(Some("1".to_string()))
+ }
+
+ #[op]
// using the same op that is used in `tsc.rs` for loading modules and reading
// files, but a slightly different implementation at build time.
fn op_load(state: &mut OpState, args: LoadArgs) -> Result<Value, AnyError> {
@@ -211,7 +219,7 @@ fn create_compiler_snapshot(
if args.specifier == build_specifier {
Ok(json!({
"data": r#"console.log("hello deno!");"#,
- "hash": "1",
+ "version": "1",
// this corresponds to `ts.ScriptKind.TypeScript`
"scriptKind": 3
}))
@@ -230,7 +238,7 @@ fn create_compiler_snapshot(
let data = std::fs::read_to_string(path)?;
Ok(json!({
"data": data,
- "hash": "1",
+ "version": "1",
// this corresponds to `ts.ScriptKind.TypeScript`
"scriptKind": 3
}))
@@ -255,6 +263,7 @@ fn create_compiler_snapshot(
op_cwd::decl(),
op_exists::decl(),
op_load::decl(),
+ op_script_version::decl(),
])
.state(move |state| {
state.put(op_crate_libs.clone());
diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs
index 0a383e781..4fb8c428e 100644
--- a/cli/lsp/documents.rs
+++ b/cli/lsp/documents.rs
@@ -200,6 +200,13 @@ impl AssetOrDocument {
}
}
+ pub fn media_type(&self) -> MediaType {
+ match self {
+ AssetOrDocument::Asset(_) => MediaType::TypeScript, // assets are always TypeScript
+ AssetOrDocument::Document(d) => d.media_type(),
+ }
+ }
+
pub fn get_maybe_dependency(
&self,
position: &lsp::Position,
diff --git a/cli/lsp/text.rs b/cli/lsp/text.rs
index 64721ebc7..33ac2048f 100644
--- a/cli/lsp/text.rs
+++ b/cli/lsp/text.rs
@@ -2,13 +2,9 @@
use deno_core::error::custom_error;
use deno_core::error::AnyError;
-use deno_core::serde_json::json;
-use deno_core::serde_json::Value;
use dissimilar::diff;
use dissimilar::Chunk;
use std::collections::HashMap;
-use std::ops::Bound;
-use std::ops::RangeBounds;
use text_size::TextRange;
use text_size::TextSize;
use tower_lsp::jsonrpc;
@@ -261,112 +257,6 @@ pub fn get_edits(a: &str, b: &str, line_index: &LineIndex) -> Vec<TextEdit> {
text_edits
}
-/// Convert a difference between two strings into a change range used by the
-/// TypeScript Language Service.
-pub fn get_range_change(a: &str, b: &str) -> Value {
- if a == b {
- return json!(null);
- }
- let chunks = diff(a, b);
- let mut iter = chunks.iter().peekable();
- let mut started = false;
- let mut start = 0;
- let mut end = 0;
- let mut new_length = 0;
- let mut equal = 0;
- let mut a_pos = 0;
- loop {
- let diff = iter.next();
- match diff {
- None => break,
- Some(Chunk::Equal(e)) => {
- a_pos += e.encode_utf16().count();
- equal += e.encode_utf16().count();
- }
- Some(Chunk::Delete(d)) => {
- if !started {
- start = a_pos;
- started = true;
- equal = 0;
- }
- a_pos += d.encode_utf16().count();
- if started {
- end = a_pos;
- new_length += equal;
- equal = 0;
- }
- }
- Some(Chunk::Insert(i)) => {
- if !started {
- start = a_pos;
- end = a_pos;
- started = true;
- equal = 0;
- } else {
- end += equal;
- }
- new_length += i.encode_utf16().count() + equal;
- equal = 0;
- }
- }
- }
-
- json!({
- "span": {
- "start": start,
- "length": end - start,
- },
- "newLength": new_length,
- })
-}
-
-/// Provide a slice of a string based on a character range.
-pub fn slice(s: &str, range: impl RangeBounds<usize>) -> &str {
- let start = match range.start_bound() {
- Bound::Included(bound) | Bound::Excluded(bound) => *bound,
- Bound::Unbounded => 0,
- };
- let len = match range.end_bound() {
- Bound::Included(bound) => *bound + 1,
- Bound::Excluded(bound) => *bound,
- Bound::Unbounded => s.encode_utf16().count(),
- } - start;
- substring(s, start, start + len)
-}
-
-/// Provide a substring based on the start and end character index positions.
-pub fn substring(s: &str, start: usize, end: usize) -> &str {
- let len = end - start;
- let mut char_pos = 0;
- let mut byte_start = 0;
- let mut it = s.chars();
- loop {
- if char_pos == start {
- break;
- }
- if let Some(c) = it.next() {
- char_pos += c.len_utf16();
- byte_start += c.len_utf8();
- } else {
- break;
- }
- }
- char_pos = 0;
- let mut byte_end = byte_start;
- loop {
- if char_pos == len {
- break;
- }
- if let Some(c) = it.next() {
- char_pos += c.len_utf16();
- byte_end += c.len_utf8();
- } else {
- break;
- }
- }
- &s[byte_start..byte_end]
-}
-
#[cfg(test)]
mod tests {
use super::*;
@@ -637,155 +527,4 @@ const C: char = \"パ パ\";
]
)
}
-
- #[test]
- fn test_get_range_change() {
- let a = "abcdefg";
- let b = "abcdefg";
- let actual = get_range_change(a, b);
- assert_eq!(actual, json!(null));
-
- let a = "abcdefg";
- let b = "abedcfg";
- let actual = get_range_change(a, b);
- assert_eq!(
- actual,
- json!({
- "span": {
- "start": 2,
- "length": 3,
- },
- "newLength": 3
- })
- );
-
- let a = "abfg";
- let b = "abcdefg";
- let actual = get_range_change(a, b);
- assert_eq!(
- actual,
- json!({
- "span": {
- "start": 2,
- "length": 0,
- },
- "newLength": 3
- })
- );
-
- let a = "abcdefg";
- let b = "abfg";
- let actual = get_range_change(a, b);
- assert_eq!(
- actual,
- json!({
- "span": {
- "start": 2,
- "length": 3,
- },
- "newLength": 0
- })
- );
-
- let a = "abcdefg";
- let b = "abfghij";
- let actual = get_range_change(a, b);
- assert_eq!(
- actual,
- json!({
- "span": {
- "start": 2,
- "length": 5,
- },
- "newLength": 5
- })
- );
-
- let a = "abcdefghijk";
- let b = "axcxexfxixk";
- let actual = get_range_change(a, b);
- assert_eq!(
- actual,
- json!({
- "span": {
- "start": 1,
- "length": 9,
- },
- "newLength": 9
- })
- );
-
- let a = "abcde";
- let b = "ab(c)de";
- let actual = get_range_change(a, b);
- assert_eq!(
- actual,
- json!({
- "span" : {
- "start": 2,
- "length": 1,
- },
- "newLength": 3
- })
- );
-
- let a = "hello πŸ¦•!";
- let b = "hello deno!";
- let actual = get_range_change(a, b);
- assert_eq!(
- actual,
- json!({
- "span": {
- "start": 6,
- "length": 2,
- },
- "newLength": 4
- })
- );
-
- let a = "hello deno!";
- let b = "hello denoπŸ¦•!";
- let actual = get_range_change(a, b);
- assert_eq!(
- actual,
- json!({
- "span": {
- "start": 10,
- "length": 0,
- },
- "newLength": 2
- })
- );
-
- // TODO(@kitsonk): https://github.com/dtolnay/dissimilar/issues/5
- // let a = r#" πŸ¦•πŸ‡ΊπŸ‡ΈπŸ‘ "#;
- // let b = r#" πŸ‡ΊπŸ‡ΈπŸ‘ "#;
- // let actual = get_range_change(a, b);
- // assert_eq!(
- // actual,
- // json!({
- // "span": {
- // "start": 1,
- // "length": 2,
- // },
- // "newLength": 0
- // })
- // );
- }
-
- #[test]
- fn test_substring() {
- assert_eq!(substring("Deno", 1, 3), "en");
- assert_eq!(substring("y̆y̆", 2, 4), "y̆");
- assert_eq!(substring("πŸ¦•πŸ¦•", 2, 4), "πŸ¦•");
- }
-
- #[test]
- fn test_slice() {
- assert_eq!(slice("Deno", 1..3), "en");
- assert_eq!(slice("Deno", 1..=3), "eno");
- assert_eq!(slice("Deno Land", 1..), "eno Land");
- assert_eq!(slice("Deno", ..3), "Den");
- assert_eq!(slice("Hello πŸ¦•", 6..8), "πŸ¦•");
- }
}
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index 27d32b2db..1448c8c96 100644
--- a/cli/lsp/tsc.rs
+++ b/cli/lsp/tsc.rs
@@ -13,7 +13,6 @@ use super::refactor::EXTRACT_INTERFACE;
use super::refactor::EXTRACT_TYPE;
use super::semantic_tokens;
use super::semantic_tokens::SemanticTokensBuilder;
-use super::text;
use super::text::LineIndex;
use super::urls::LspUrlMap;
use super::urls::INVALID_SPECIFIER;
@@ -47,7 +46,6 @@ use log::warn;
use once_cell::sync::Lazy;
use regex::Captures;
use regex::Regex;
-use std::borrow::Cow;
use std::cmp;
use std::collections::HashMap;
use std::collections::HashSet;
@@ -157,7 +155,6 @@ impl TsServer {
struct AssetDocumentInner {
specifier: ModuleSpecifier,
text: Arc<String>,
- length: usize,
line_index: Arc<LineIndex>,
maybe_navigation_tree: Option<Arc<NavigationTree>>,
}
@@ -173,7 +170,6 @@ impl AssetDocument {
Self(Arc::new(AssetDocumentInner {
specifier,
text: Arc::new(text.to_string()),
- length: text.encode_utf16().count(),
line_index: Arc::new(LineIndex::new(text)),
maybe_navigation_tree: None,
}))
@@ -197,14 +193,6 @@ impl AssetDocument {
self.0.text.clone()
}
- pub fn text_str(&self) -> &str {
- self.0.text.as_str()
- }
-
- pub fn length(&self) -> usize {
- self.0.length
- }
-
pub fn line_index(&self) -> Arc<LineIndex> {
self.0.line_index.clone()
}
@@ -2374,17 +2362,16 @@ struct Response {
data: Value,
}
-struct State<'a> {
+struct State {
last_id: usize,
performance: Arc<Performance>,
response: Option<Response>,
state_snapshot: Arc<StateSnapshot>,
- snapshots: HashMap<(ModuleSpecifier, Cow<'a, str>), String>,
specifiers: HashMap<String, String>,
token: CancellationToken,
}
-impl<'a> State<'a> {
+impl State {
fn new(
state_snapshot: Arc<StateSnapshot>,
performance: Arc<Performance>,
@@ -2394,7 +2381,6 @@ impl<'a> State<'a> {
performance,
response: None,
state_snapshot,
- snapshots: HashMap::default(),
specifiers: HashMap::default(),
token: Default::default(),
}
@@ -2426,31 +2412,37 @@ impl<'a> State<'a> {
}
ModuleSpecifier::parse(&specifier_str).map_err(|err| err.into())
}
-}
-/// If a snapshot is missing from the state cache, add it.
-fn cache_snapshot(
- state: &mut State,
- specifier: &ModuleSpecifier,
- version: String,
-) -> Result<(), AnyError> {
- if !state
- .snapshots
- .contains_key(&(specifier.clone(), version.clone().into()))
- {
- let content = state
- .state_snapshot
- .documents
- .get(specifier)
- .ok_or_else(|| {
- anyhow!("Specifier unexpectedly doesn't exist: {}", specifier)
- })?
- .content();
- state
- .snapshots
- .insert((specifier.clone(), version.into()), content.to_string());
+ fn get_asset_or_document(
+ &self,
+ specifier: &ModuleSpecifier,
+ ) -> Option<AssetOrDocument> {
+ let snapshot = &self.state_snapshot;
+ if specifier.scheme() == "asset" {
+ snapshot.assets.get(specifier).map(AssetOrDocument::Asset)
+ } else {
+ snapshot
+ .documents
+ .get(specifier)
+ .map(AssetOrDocument::Document)
+ }
+ }
+
+ fn script_version(&self, specifier: &ModuleSpecifier) -> Option<String> {
+ if specifier.scheme() == "asset" {
+ if self.state_snapshot.assets.contains_key(specifier) {
+ Some("1".to_string())
+ } else {
+ None
+ }
+ } else {
+ self
+ .state_snapshot
+ .documents
+ .get(specifier)
+ .map(|d| d.script_version())
+ }
}
- Ok(())
}
fn normalize_specifier<S: AsRef<str>>(
@@ -2462,28 +2454,6 @@ fn normalize_specifier<S: AsRef<str>>(
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
-struct SourceSnapshotArgs {
- specifier: String,
- version: String,
-}
-
-/// The language service is dropping a reference to a source file snapshot, and
-/// we can drop our version of that document.
-#[op]
-fn op_dispose(
- state: &mut OpState,
- args: SourceSnapshotArgs,
-) -> Result<bool, AnyError> {
- let state = state.borrow_mut::<State>();
- let mark = state.performance.mark("op_dispose", Some(&args));
- let specifier = state.normalize_specifier(&args.specifier)?;
- state.snapshots.remove(&(specifier, args.version.into()));
- state.performance.measure(mark);
- Ok(true)
-}
-
-#[derive(Debug, Deserialize, Serialize)]
-#[serde(rename_all = "camelCase")]
struct SpecifierArgs {
specifier: String,
}
@@ -2506,116 +2476,6 @@ fn op_exists(state: &mut OpState, args: SpecifierArgs) -> bool {
state.state_snapshot.documents.exists(&specifier)
}
-#[derive(Debug, Deserialize, Serialize)]
-#[serde(rename_all = "camelCase")]
-struct GetChangeRangeArgs {
- specifier: String,
- old_length: u32,
- old_version: String,
- version: String,
-}
-
-/// The language service wants to compare an old snapshot with a new snapshot to
-/// determine what source has changed.
-#[op]
-fn op_get_change_range(
- state: &mut OpState,
- args: GetChangeRangeArgs,
-) -> Result<Value, AnyError> {
- let state = state.borrow_mut::<State>();
- let mark = state.performance.mark("op_get_change_range", Some(&args));
- let specifier = state.normalize_specifier(&args.specifier)?;
- cache_snapshot(state, &specifier, args.version.clone())?;
- let r = if let Some(current) = state
- .snapshots
- .get(&(specifier.clone(), args.version.clone().into()))
- {
- if let Some(prev) = state
- .snapshots
- .get(&(specifier, args.old_version.clone().into()))
- {
- Ok(text::get_range_change(prev, current))
- } else {
- let new_length = current.encode_utf16().count();
- // when a local file is opened up in the editor, the compiler might
- // already have a snapshot of it in memory, and will request it, but we
- // now are working off in memory versions of the document, and so need
- // to tell tsc to reset the whole document
- Ok(json!({
- "span": {
- "start": 0,
- "length": args.old_length,
- },
- "newLength": new_length,
- }))
- }
- } else {
- Err(custom_error(
- "MissingSnapshot",
- format!(
- "The current snapshot version is missing.\n Args: \"{:?}\"",
- args
- ),
- ))
- };
-
- state.performance.measure(mark);
- r
-}
-
-#[op]
-fn op_get_length(
- state: &mut OpState,
- args: SourceSnapshotArgs,
-) -> Result<usize, AnyError> {
- let state = state.borrow_mut::<State>();
- let mark = state.performance.mark("op_get_length", Some(&args));
- let specifier = state.normalize_specifier(args.specifier)?;
- let r = if let Some(asset) = state.state_snapshot.assets.get(&specifier) {
- Ok(asset.length())
- } else {
- cache_snapshot(state, &specifier, args.version.clone())?;
- let content = state
- .snapshots
- .get(&(specifier, args.version.into()))
- .unwrap();
- Ok(content.encode_utf16().count())
- };
- state.performance.measure(mark);
- r
-}
-
-#[derive(Debug, Deserialize, Serialize)]
-#[serde(rename_all = "camelCase")]
-struct GetTextArgs {
- specifier: String,
- version: String,
- start: usize,
- end: usize,
-}
-
-#[op]
-fn op_get_text(
- state: &mut OpState,
- args: GetTextArgs,
-) -> Result<String, AnyError> {
- let state = state.borrow_mut::<State>();
- let mark = state.performance.mark("op_get_text", Some(&args));
- let specifier = state.normalize_specifier(args.specifier)?;
- let maybe_asset = state.state_snapshot.assets.get(&specifier);
- let content = if let Some(content) = &maybe_asset {
- content.text_str()
- } else {
- cache_snapshot(state, &specifier, args.version.clone())?;
- state
- .snapshots
- .get(&(specifier, args.version.into()))
- .unwrap()
- };
- state.performance.measure(mark);
- Ok(text::slice(content, args.start..args.end).to_string())
-}
-
#[op]
fn op_is_cancelled(state: &mut OpState) -> bool {
let state = state.borrow_mut::<State>();
@@ -2626,13 +2486,22 @@ fn op_is_cancelled(state: &mut OpState) -> bool {
fn op_load(
state: &mut OpState,
args: SpecifierArgs,
-) -> Result<Option<String>, AnyError> {
+) -> Result<Value, AnyError> {
let state = state.borrow_mut::<State>();
let mark = state.performance.mark("op_load", Some(&args));
let specifier = state.normalize_specifier(args.specifier)?;
- let document = state.state_snapshot.documents.get(&specifier);
+ let asset_or_document = state.get_asset_or_document(&specifier);
state.performance.measure(mark);
- Ok(document.map(|d| d.content().to_string()))
+ Ok(match asset_or_document {
+ Some(doc) => {
+ json!({
+ "data": doc.text(),
+ "scriptKind": crate::tsc::as_ts_script_kind(&doc.media_type()),
+ "version": state.script_version(&specifier),
+ })
+ }
+ None => Value::Null,
+ })
}
#[op]
@@ -2705,20 +2574,7 @@ fn op_script_version(
// this op is very "noisy" and measuring its performance is not useful, so we
// don't measure it uniquely anymore.
let specifier = state.normalize_specifier(args.specifier)?;
- if specifier.scheme() == "asset" {
- if state.state_snapshot.assets.contains_key(&specifier) {
- Ok(Some("1".to_string()))
- } else {
- Ok(None)
- }
- } else {
- let script_version = state
- .state_snapshot
- .documents
- .get(&specifier)
- .map(|d| d.script_version());
- Ok(script_version)
- }
+ Ok(state.script_version(&specifier))
}
/// Create and setup a JsRuntime based on a snapshot. It is expected that the
@@ -2735,11 +2591,7 @@ fn js_runtime(performance: Arc<Performance>) -> JsRuntime {
fn init_extension(performance: Arc<Performance>) -> Extension {
Extension::builder()
.ops(vec![
- op_dispose::decl(),
op_exists::decl(),
- op_get_change_range::decl(),
- op_get_length::decl(),
- op_get_text::decl(),
op_is_cancelled::decl(),
op_load::decl(),
op_resolve::decl(),
diff --git a/cli/tests/integration/lsp_tests.rs b/cli/tests/integration/lsp_tests.rs
index 08554bac8..be2283080 100644
--- a/cli/tests/integration/lsp_tests.rs
+++ b/cli/tests/integration/lsp_tests.rs
@@ -1401,7 +1401,9 @@ fn lsp_hover_change_mbc() {
},
"end": {
"line": 1,
- "character": 13
+ // the LSP uses utf16 encoded characters indexes, so
+ // after the deno emoiji is character index 15
+ "character": 15
}
},
"text": ""
@@ -1425,7 +1427,7 @@ fn lsp_hover_change_mbc() {
},
"position": {
"line": 2,
- "character": 14
+ "character": 15
}
}),
)
@@ -1444,11 +1446,11 @@ fn lsp_hover_change_mbc() {
"range": {
"start": {
"line": 2,
- "character": 13,
+ "character": 15,
},
"end": {
"line": 2,
- "character": 14,
+ "character": 16,
},
}
}))
@@ -4284,7 +4286,7 @@ fn lsp_performance() {
.unwrap();
assert!(maybe_err.is_none());
if let Some(res) = maybe_res {
- assert_eq!(res.averages.len(), 14);
+ assert_eq!(res.averages.len(), 13);
} else {
panic!("unexpected result");
}
diff --git a/cli/tsc.rs b/cli/tsc.rs
index aba289d8c..d629fb80b 100644
--- a/cli/tsc.rs
+++ b/cli/tsc.rs
@@ -422,7 +422,7 @@ struct LoadArgs {
specifier: String,
}
-fn as_ts_script_kind(media_type: &MediaType) -> i32 {
+pub fn as_ts_script_kind(media_type: &MediaType) -> i32 {
match media_type {
MediaType::JavaScript => 1,
MediaType::Jsx => 2,
@@ -492,7 +492,7 @@ fn op_load(state: &mut OpState, args: Value) -> Result<Value, AnyError> {
};
Ok(
- json!({ "data": data, "hash": hash, "scriptKind": as_ts_script_kind(&media_type) }),
+ json!({ "data": data, "version": hash, "scriptKind": as_ts_script_kind(&media_type) }),
)
}
@@ -1002,7 +1002,7 @@ mod tests {
actual,
json!({
"data": "console.log(\"hello deno\");\n",
- "hash": "149c777056afcc973d5fcbe11421b6d5ddc57b81786765302030d7fc893bf729",
+ "version": "149c777056afcc973d5fcbe11421b6d5ddc57b81786765302030d7fc893bf729",
"scriptKind": 3,
})
);
@@ -1012,7 +1012,7 @@ mod tests {
#[serde(rename_all = "camelCase")]
struct LoadResponse {
data: String,
- hash: Option<String>,
+ version: Option<String>,
script_kind: i64,
}
@@ -1033,7 +1033,7 @@ mod tests {
serde_json::from_value(value).expect("failed to deserialize");
let expected = get_asset("lib.dom.d.ts").unwrap();
assert_eq!(actual.data, expected);
- assert!(actual.hash.is_some());
+ assert!(actual.version.is_some());
assert_eq!(actual.script_kind, 3);
}
@@ -1052,7 +1052,7 @@ mod tests {
actual,
json!({
"data": "some content",
- "hash": null,
+ "version": null,
"scriptKind": 0,
})
);
@@ -1070,7 +1070,7 @@ mod tests {
actual,
json!({
"data": null,
- "hash": null,
+ "version": null,
"scriptKind": 0,
})
)
diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js
index 68f4f37f6..6d47b6ac7 100644
--- a/cli/tsc/99_main_compiler.js
+++ b/cli/tsc/99_main_compiler.js
@@ -72,6 +72,8 @@ delete Object.prototype.__proto__;
}
}
+ // In the case of the LSP, this is initialized with the assets
+ // when snapshotting and never added to or removed after that.
/** @type {Map<string, ts.SourceFile>} */
const sourceFileCache = new Map();
@@ -186,62 +188,6 @@ delete Object.prototype.__proto__;
target: ts.ScriptTarget.ESNext,
};
- class ScriptSnapshot {
- /** @type {string} */
- specifier;
- /** @type {string} */
- version;
- /**
- * @param {string} specifier
- * @param {string} version
- */
- constructor(specifier, version) {
- this.specifier = specifier;
- this.version = version;
- }
- /**
- * @param {number} start
- * @param {number} end
- * @returns {string}
- */
- getText(start, end) {
- const { specifier, version } = this;
- debug(
- `snapshot.getText(${start}, ${end}) specifier: ${specifier} version: ${version}`,
- );
- return core.opSync("op_get_text", { specifier, version, start, end });
- }
- /**
- * @returns {number}
- */
- getLength() {
- const { specifier, version } = this;
- debug(`snapshot.getLength() specifier: ${specifier} version: ${version}`);
- return core.opSync("op_get_length", { specifier, version });
- }
- /**
- * @param {ScriptSnapshot} oldSnapshot
- * @returns {ts.TextChangeRange | undefined}
- */
- getChangeRange(oldSnapshot) {
- const { specifier, version } = this;
- const { version: oldVersion } = oldSnapshot;
- const oldLength = oldSnapshot.getLength();
- debug(
- `snapshot.getLength() specifier: ${specifier} oldVersion: ${oldVersion} version: ${version}`,
- );
- return core.opSync(
- "op_get_change_range",
- { specifier, oldLength, oldVersion, version },
- );
- }
- dispose() {
- const { specifier, version } = this;
- debug(`snapshot.dispose() specifier: ${specifier} version: ${version}`);
- core.opSync("op_dispose", { specifier, version });
- }
- }
-
/** Error thrown on cancellation. */
class OperationCanceledError extends Error {
}
@@ -310,16 +256,17 @@ delete Object.prototype.__proto__;
ts.ScriptTarget[languageVersion]
})`,
);
+
+ // Needs the original specifier
+ specifier = normalizedToOriginalMap.get(specifier) ?? specifier;
+
let sourceFile = sourceFileCache.get(specifier);
if (sourceFile) {
return sourceFile;
}
- // Needs the original specifier
- specifier = normalizedToOriginalMap.get(specifier) ?? specifier;
-
- /** @type {{ data: string; hash?: string; scriptKind: ts.ScriptKind }} */
- const { data, hash, scriptKind } = core.opSync(
+ /** @type {{ data: string; scriptKind: ts.ScriptKind }} */
+ const { data, scriptKind, version } = core.opSync(
"op_load",
{ specifier },
);
@@ -335,8 +282,9 @@ delete Object.prototype.__proto__;
scriptKind,
);
sourceFile.moduleName = specifier;
- sourceFile.version = hash;
+ sourceFile.version = version;
sourceFileCache.set(specifier, sourceFile);
+ scriptVersionCache.set(specifier, version);
return sourceFile;
},
getDefaultLibFileName() {
@@ -445,11 +393,17 @@ delete Object.prototype.__proto__;
},
};
}
- const version = host.getScriptVersion(specifier);
- if (version != null) {
- return new ScriptSnapshot(specifier, version);
+
+ const fileInfo = core.opSync(
+ "op_load",
+ { specifier },
+ );
+ if (fileInfo) {
+ scriptVersionCache.set(specifier, fileInfo.version);
+ return ts.ScriptSnapshot.fromString(fileInfo.data);
+ } else {
+ return undefined;
}
- return undefined;
},
};