0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-02-08 07:16:56 -05:00

rebase fixup

This commit is contained in:
Nathan Whitaker 2025-01-30 15:20:08 -08:00
parent 3fc6cee6a0
commit 3afc00c30c
4 changed files with 214 additions and 124 deletions

View file

@ -2378,7 +2378,7 @@ impl Inner {
let scope = asset_or_doc.scope();
let maybe_completion_info = self
.ts_server
.get_completions_tsc(
.get_completions(
self.snapshot(),
specifier.clone(),
position,

View file

@ -1,4 +1,4 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// Copyright 2018-2025 the Deno authors. MIT license.
use std::fmt;

View file

@ -6,6 +6,7 @@ use std::collections::BTreeMap;
use std::collections::HashMap;
use std::collections::HashSet;
use std::convert::Infallible;
use std::ffi::c_void;
use std::net::SocketAddr;
use std::ops::Range;
use std::path::Path;
@ -64,6 +65,7 @@ use tokio_util::sync::CancellationToken;
use tower_lsp::jsonrpc::Error as LspError;
use tower_lsp::jsonrpc::Result as LspResult;
use tower_lsp::lsp_types as lsp;
use tracing_opentelemetry::OpenTelemetrySpanExt;
use super::analysis::CodeActionData;
use super::code_lens;
@ -123,6 +125,7 @@ type Request = (
oneshot::Sender<Result<String, AnyError>>,
CancellationToken,
Option<PendingChange>,
Option<opentelemetry::Context>,
);
#[derive(Debug, Clone, Copy, Serialize_repr)]
@ -466,6 +469,7 @@ impl TsServer {
}
}
#[tracing::instrument(skip_all)]
pub async fn get_diagnostics(
&self,
snapshot: Arc<StateSnapshot>,
@ -526,6 +530,7 @@ impl TsServer {
Ok((diagnostics_map, ambient_modules_by_scope))
}
#[tracing::instrument(skip_all)]
pub async fn cleanup_semantic_cache(&self, snapshot: Arc<StateSnapshot>) {
for scope in snapshot
.config
@ -547,6 +552,7 @@ impl TsServer {
}
}
#[tracing::instrument(skip_all)]
pub async fn find_references(
&self,
snapshot: Arc<StateSnapshot>,
@ -596,6 +602,7 @@ impl TsServer {
Ok(Some(all_symbols.into_iter().collect()))
}
#[tracing::instrument(skip_all)]
pub async fn get_navigation_tree(
&self,
snapshot: Arc<StateSnapshot>,
@ -608,6 +615,7 @@ impl TsServer {
self.request(snapshot, req, scope).await
}
#[tracing::instrument(skip_all)]
pub async fn get_supported_code_fixes(
&self,
snapshot: Arc<StateSnapshot>,
@ -619,6 +627,7 @@ impl TsServer {
})
}
#[tracing::instrument(skip_all)]
pub async fn get_quick_info(
&self,
snapshot: Arc<StateSnapshot>,
@ -637,6 +646,7 @@ impl TsServer {
}
#[allow(clippy::too_many_arguments)]
#[tracing::instrument(skip_all)]
pub async fn get_code_fixes(
&self,
snapshot: Arc<StateSnapshot>,
@ -678,6 +688,7 @@ impl TsServer {
}
#[allow(clippy::too_many_arguments)]
#[tracing::instrument(skip_all)]
pub async fn get_applicable_refactors(
&self,
snapshot: Arc<StateSnapshot>,
@ -706,6 +717,7 @@ impl TsServer {
})
}
#[tracing::instrument(skip_all)]
pub async fn get_combined_code_fix(
&self,
snapshot: Arc<StateSnapshot>,
@ -737,6 +749,7 @@ impl TsServer {
}
#[allow(clippy::too_many_arguments)]
#[tracing::instrument(skip_all)]
pub async fn get_edits_for_refactor(
&self,
snapshot: Arc<StateSnapshot>,
@ -769,6 +782,7 @@ impl TsServer {
})
}
#[tracing::instrument(skip_all)]
pub async fn get_edits_for_file_rename(
&self,
snapshot: Arc<StateSnapshot>,
@ -819,6 +833,7 @@ impl TsServer {
Ok(all_changes.into_iter().collect())
}
#[tracing::instrument(skip_all)]
pub async fn get_document_highlights(
&self,
snapshot: Arc<StateSnapshot>,
@ -841,6 +856,7 @@ impl TsServer {
})
}
#[tracing::instrument(skip_all)]
pub async fn get_definition(
&self,
snapshot: Arc<StateSnapshot>,
@ -867,6 +883,7 @@ impl TsServer {
})
}
#[tracing::instrument(skip_all)]
pub async fn get_type_definition(
&self,
snapshot: Arc<StateSnapshot>,
@ -893,6 +910,7 @@ impl TsServer {
})
}
#[tracing::instrument(skip_all)]
pub async fn get_completions(
&self,
snapshot: Arc<StateSnapshot>,
@ -919,6 +937,7 @@ impl TsServer {
})
}
#[tracing::instrument(skip_all)]
pub async fn get_completion_details(
&self,
snapshot: Arc<StateSnapshot>,
@ -945,6 +964,7 @@ impl TsServer {
})
}
#[tracing::instrument(skip_all)]
pub async fn get_implementations(
&self,
snapshot: Arc<StateSnapshot>,
@ -994,6 +1014,7 @@ impl TsServer {
Ok(Some(all_locations.into_iter().collect()))
}
#[tracing::instrument(skip_all)]
pub async fn get_outlining_spans(
&self,
snapshot: Arc<StateSnapshot>,
@ -1009,6 +1030,7 @@ impl TsServer {
})
}
#[tracing::instrument(skip_all)]
pub async fn provide_call_hierarchy_incoming_calls(
&self,
snapshot: Arc<StateSnapshot>,
@ -1052,6 +1074,7 @@ impl TsServer {
Ok(all_calls.into_iter().collect())
}
#[tracing::instrument(skip_all)]
pub async fn provide_call_hierarchy_outgoing_calls(
&self,
snapshot: Arc<StateSnapshot>,
@ -1078,6 +1101,7 @@ impl TsServer {
})
}
#[tracing::instrument(skip_all)]
pub async fn prepare_call_hierarchy(
&self,
snapshot: Arc<StateSnapshot>,
@ -1112,6 +1136,7 @@ impl TsServer {
})
}
#[tracing::instrument(skip_all)]
pub async fn find_rename_locations(
&self,
snapshot: Arc<StateSnapshot>,
@ -1164,6 +1189,7 @@ impl TsServer {
Ok(Some(all_locations.into_iter().collect()))
}
#[tracing::instrument(skip_all)]
pub async fn get_smart_selection_range(
&self,
snapshot: Arc<StateSnapshot>,
@ -1181,6 +1207,7 @@ impl TsServer {
})
}
#[tracing::instrument(skip_all)]
pub async fn get_encoded_semantic_classifications(
&self,
snapshot: Arc<StateSnapshot>,
@ -1202,6 +1229,7 @@ impl TsServer {
})
}
#[tracing::instrument(skip_all)]
pub async fn get_signature_help_items(
&self,
snapshot: Arc<StateSnapshot>,
@ -1221,6 +1249,7 @@ impl TsServer {
})
}
#[tracing::instrument(skip_all)]
pub async fn get_navigate_to_items(
&self,
snapshot: Arc<StateSnapshot>,
@ -1264,6 +1293,7 @@ impl TsServer {
Ok(all_items.into_iter().collect())
}
#[tracing::instrument(skip_all)]
pub async fn provide_inlay_hints(
&self,
snapshot: Arc<StateSnapshot>,
@ -1312,6 +1342,8 @@ impl TsServer {
where
R: de::DeserializeOwned,
{
let context = tracing::Span::current().context();
// When an LSP request is cancelled by the client, the future this is being
// executed under and any local variables here will be dropped at the next
// await point. To pass on that cancellation to the TS thread, we use drop_guard
@ -1323,7 +1355,15 @@ impl TsServer {
if self
.sender
.send((req, scope, snapshot, tx, token.clone(), change))
.send((
req,
scope,
snapshot,
tx,
token.clone(),
change,
Some(context),
))
.is_err()
{
return Err(anyhow!("failed to send request to tsc thread"));
@ -1332,6 +1372,7 @@ impl TsServer {
value = &mut rx => {
let value = value??;
droppable_token.disarm();
let _sp = tracing::info_span!("deserialize TSC response").entered();
Ok(serde_json::from_str(&value)?)
}
_ = token.cancelled() => {
@ -3685,12 +3726,14 @@ pub struct CompletionInfo {
}
impl CompletionInfo {
#[tracing::instrument(skip_all)]
fn normalize(&mut self, specifier_map: &TscSpecifierMap) {
for entry in &mut self.entries {
entry.normalize(specifier_map);
}
}
#[tracing::instrument(skip_all, fields(entries = %self.entries.len()))]
pub fn as_completion_response(
&self,
line_index: Arc<LineIndex>,
@ -4401,6 +4444,7 @@ struct State {
token: CancellationToken,
pending_requests: Option<UnboundedReceiver<Request>>,
mark: Option<PerformanceMark>,
context: Option<opentelemetry::Context>,
}
impl State {
@ -4420,6 +4464,7 @@ impl State {
token: Default::default(),
mark: None,
pending_requests: Some(pending_requests),
context: None,
}
}
@ -4500,6 +4545,7 @@ fn op_load<'s>(
state: &mut OpState,
#[string] specifier: &str,
) -> Result<v8::Local<'s, v8::Value>, LoadError> {
let _span = tracing::info_span!("op_load").entered();
let state = state.borrow_mut::<State>();
let mark = state
.performance
@ -4531,6 +4577,7 @@ fn op_release(
state: &mut OpState,
#[string] specifier: &str,
) -> Result<(), deno_core::url::ParseError> {
let _span = tracing::info_span!("op_release").entered();
let state = state.borrow_mut::<State>();
let mark = state
.performance
@ -4549,6 +4596,7 @@ fn op_resolve(
#[string] base: String,
#[serde] specifiers: Vec<(bool, String)>,
) -> Result<Vec<Option<(String, Option<String>)>>, deno_core::url::ParseError> {
let _span = tracing::info_span!("op_resolve").entered();
op_resolve_inner(state, ResolveArgs { base, specifiers })
}
@ -4600,7 +4648,7 @@ async fn op_poll_requests(
state.pending_requests.take().unwrap()
};
let Some((request, scope, snapshot, response_tx, token, change)) =
let Some((request, scope, snapshot, response_tx, token, change, context)) =
pending_requests.recv().await
else {
return None.into();
@ -4619,6 +4667,7 @@ async fn op_poll_requests(
.performance
.mark_with_args(format!("tsc.host.{}", request.method()), &request);
state.mark = Some(mark);
state.context = context;
Some(TscRequestArray {
request,
@ -4666,6 +4715,7 @@ fn op_respond(
#[string] response: String,
#[string] error: String,
) {
let _span = tracing::info_span!("op_respond").entered();
let state = state.borrow_mut::<State>();
state.performance.measure(state.mark.take().unwrap());
state.last_scope = None;
@ -4683,6 +4733,37 @@ fn op_respond(
}
}
struct TracingSpan(#[allow(dead_code)] tracing::span::EnteredSpan);
// struct TracingSpan(#[allow(dead_code)] ());
deno_core::external!(TracingSpan, "tracingspan");
#[op2(fast)]
fn op_make_span(
op_state: &mut OpState,
#[string] s: &str,
needs_context: bool,
) -> *const c_void {
let state = op_state.borrow_mut::<State>();
let sp = tracing::info_span!("js", otel.name = format!("js::{s}").as_str());
let span = if needs_context {
span_with_context(state, sp)
} else {
sp.entered()
};
deno_core::ExternalPointer::new(TracingSpan(span)).into_raw()
}
#[op2(fast)]
fn op_exit_span(op_state: &mut OpState, span: *const c_void, root: bool) {
let ptr = deno_core::ExternalPointer::<TracingSpan>::from_raw(span);
let _span = unsafe { ptr.unsafely_take().0 };
if root {
let state = op_state.borrow_mut::<State>();
state.context.pop();
}
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct ScriptNames {
@ -4693,6 +4774,7 @@ struct ScriptNames {
#[op2]
#[serde]
fn op_script_names(state: &mut OpState) -> ScriptNames {
let _span = tracing::info_span!("op_script_names").entered();
let state = state.borrow_mut::<State>();
let mark = state.performance.mark("tsc.op.op_script_names");
let mut result = ScriptNames {
@ -4960,6 +5042,8 @@ deno_core::extension!(deno_tsc,
op_script_version,
op_project_version,
op_poll_requests,
op_make_span,
op_exit_span,
],
options = {
performance: Arc<Performance>,

View file

@ -384,140 +384,146 @@ function arraysEqual(a, b) {
* @param {PendingChange | null} maybeChange
*/
function serverRequest(id, method, args, scope, maybeChange) {
debug(`serverRequest()`, id, method, args, scope, maybeChange);
if (maybeChange !== null) {
const changedScripts = maybeChange[0];
const newProjectVersion = maybeChange[1];
const newConfigsByScope = maybeChange[2];
if (newConfigsByScope) {
IS_NODE_SOURCE_FILE_CACHE.clear();
ASSET_SCOPES.clear();
/** @type { typeof LANGUAGE_SERVICE_ENTRIES.byScope } */
const newByScope = new Map();
for (const [scope, config] of newConfigsByScope) {
LAST_REQUEST_SCOPE.set(scope);
const oldEntry = LANGUAGE_SERVICE_ENTRIES.byScope.get(scope);
const ls = oldEntry
? oldEntry.ls
: ts.createLanguageService(host, documentRegistry);
const compilerOptions = lspTsConfigToCompilerOptions(config);
newByScope.set(scope, { ls, compilerOptions });
LANGUAGE_SERVICE_ENTRIES.byScope.delete(scope);
}
for (const oldEntry of LANGUAGE_SERVICE_ENTRIES.byScope.values()) {
oldEntry.ls.dispose();
}
LANGUAGE_SERVICE_ENTRIES.byScope = newByScope;
}
PROJECT_VERSION_CACHE.set(newProjectVersion);
let opened = false;
let closed = false;
for (const { 0: script, 1: changeKind } of changedScripts) {
if (changeKind === ChangeKind.Opened) {
opened = true;
} else if (changeKind === ChangeKind.Closed) {
closed = true;
}
SCRIPT_VERSION_CACHE.delete(script);
SCRIPT_SNAPSHOT_CACHE.delete(script);
}
if (newConfigsByScope || opened || closed) {
clearScriptNamesCache();
}
}
// For requests pertaining to an asset document, we make it so that the
// passed scope is just its own specifier. We map it to an actual scope here
// based on the first scope that the asset was loaded into.
if (scope?.startsWith(ASSETS_URL_PREFIX)) {
scope = ASSET_SCOPES.get(scope) ?? null;
}
LAST_REQUEST_METHOD.set(method);
LAST_REQUEST_SCOPE.set(scope);
const ls = (scope ? LANGUAGE_SERVICE_ENTRIES.byScope.get(scope)?.ls : null) ??
LANGUAGE_SERVICE_ENTRIES.unscoped.ls;
switch (method) {
case "$getSupportedCodeFixes": {
return respond(
id,
ts.getSupportedCodeFixes(),
);
}
case "$getAssets": {
return respond(id, getAssets());
}
case "$getDiagnostics": {
const projectVersion = args[1];
// there's a possibility that we receive a change notification
// but the diagnostic server queues a `$getDiagnostics` request
// with a stale project version. in that case, treat it as cancelled
// (it's about to be invalidated anyway).
const cachedProjectVersion = PROJECT_VERSION_CACHE.get();
if (cachedProjectVersion && projectVersion !== cachedProjectVersion) {
return respond(id, [{}, null]);
}
try {
/** @type {Record<string, any[]>} */
const diagnosticMap = {};
for (const specifier of args[0]) {
diagnosticMap[specifier] = fromTypeScriptDiagnostics([
...ls.getSemanticDiagnostics(specifier),
...ls.getSuggestionDiagnostics(specifier),
...ls.getSyntacticDiagnostics(specifier),
].filter(filterMapDiagnostic));
const span = ops.op_make_span(`serverRequest(${method})`, true);
try {
debug(`serverRequest()`, id, method, args, scope, maybeChange);
if (maybeChange !== null) {
const changedScripts = maybeChange[0];
const newProjectVersion = maybeChange[1];
const newConfigsByScope = maybeChange[2];
if (newConfigsByScope) {
IS_NODE_SOURCE_FILE_CACHE.clear();
ASSET_SCOPES.clear();
/** @type { typeof LANGUAGE_SERVICE_ENTRIES.byScope } */
const newByScope = new Map();
for (const [scope, config] of newConfigsByScope) {
LAST_REQUEST_SCOPE.set(scope);
const oldEntry = LANGUAGE_SERVICE_ENTRIES.byScope.get(scope);
const ls = oldEntry
? oldEntry.ls
: ts.createLanguageService(host, documentRegistry);
const compilerOptions = lspTsConfigToCompilerOptions(config);
newByScope.set(scope, { ls, compilerOptions });
LANGUAGE_SERVICE_ENTRIES.byScope.delete(scope);
}
let ambient =
ls.getProgram()?.getTypeChecker().getAmbientModules().map((symbol) =>
symbol.getName()
) ?? [];
const previousAmbient = ambientModulesCacheByScope.get(scope);
if (
ambient && previousAmbient && arraysEqual(ambient, previousAmbient)
) {
ambient = null; // null => use previous value
} else {
ambientModulesCacheByScope.set(scope, ambient);
for (const oldEntry of LANGUAGE_SERVICE_ENTRIES.byScope.values()) {
oldEntry.ls.dispose();
}
return respond(id, [diagnosticMap, ambient]);
} catch (e) {
if (
!isCancellationError(e)
) {
return respond(
id,
[{}, null],
formatErrorWithArgs(e, [id, method, args, scope, maybeChange]),
);
LANGUAGE_SERVICE_ENTRIES.byScope = newByScope;
}
PROJECT_VERSION_CACHE.set(newProjectVersion);
let opened = false;
let closed = false;
for (const { 0: script, 1: changeKind } of changedScripts) {
if (changeKind === ChangeKind.Opened) {
opened = true;
} else if (changeKind === ChangeKind.Closed) {
closed = true;
}
return respond(id, [{}, null]);
SCRIPT_VERSION_CACHE.delete(script);
SCRIPT_SNAPSHOT_CACHE.delete(script);
}
if (newConfigsByScope || opened || closed) {
clearScriptNamesCache();
}
}
default:
if (typeof ls[method] === "function") {
// The `getCompletionEntryDetails()` method returns null if the
// `source` is `null` for whatever reason. It must be `undefined`.
if (method == "getCompletionEntryDetails") {
args[4] ??= undefined;
// For requests pertaining to an asset document, we make it so that the
// passed scope is just its own specifier. We map it to an actual scope here
// based on the first scope that the asset was loaded into.
if (scope?.startsWith(ASSETS_URL_PREFIX)) {
scope = ASSET_SCOPES.get(scope) ?? null;
}
LAST_REQUEST_METHOD.set(method);
LAST_REQUEST_SCOPE.set(scope);
const ls =
(scope ? LANGUAGE_SERVICE_ENTRIES.byScope.get(scope)?.ls : null) ??
LANGUAGE_SERVICE_ENTRIES.unscoped.ls;
switch (method) {
case "$getSupportedCodeFixes": {
return respond(
id,
ts.getSupportedCodeFixes(),
);
}
case "$getAssets": {
return respond(id, getAssets());
}
case "$getDiagnostics": {
const projectVersion = args[1];
// there's a possibility that we receive a change notification
// but the diagnostic server queues a `$getDiagnostics` request
// with a stale project version. in that case, treat it as cancelled
// (it's about to be invalidated anyway).
const cachedProjectVersion = PROJECT_VERSION_CACHE.get();
if (cachedProjectVersion && projectVersion !== cachedProjectVersion) {
return respond(id, [{}, null]);
}
try {
return respond(id, ls[method](...args));
/** @type {Record<string, any[]>} */
const diagnosticMap = {};
for (const specifier of args[0]) {
diagnosticMap[specifier] = fromTypeScriptDiagnostics([
...ls.getSemanticDiagnostics(specifier),
...ls.getSuggestionDiagnostics(specifier),
...ls.getSyntacticDiagnostics(specifier),
].filter(filterMapDiagnostic));
}
let ambient =
ls.getProgram()?.getTypeChecker().getAmbientModules().map((
symbol,
) => symbol.getName()) ?? [];
const previousAmbient = ambientModulesCacheByScope.get(scope);
if (
ambient && previousAmbient && arraysEqual(ambient, previousAmbient)
) {
ambient = null; // null => use previous value
} else {
ambientModulesCacheByScope.set(scope, ambient);
}
return respond(id, [diagnosticMap, ambient]);
} catch (e) {
if (!isCancellationError(e)) {
if (
!isCancellationError(e)
) {
return respond(
id,
null,
[{}, null],
formatErrorWithArgs(e, [id, method, args, scope, maybeChange]),
);
}
return respond(id);
return respond(id, [{}, null]);
}
}
throw new TypeError(
// @ts-ignore exhausted case statement sets type to never
`Invalid request method for request: "${method}" (${id})`,
);
default:
if (typeof ls[method] === "function") {
// The `getCompletionEntryDetails()` method returns null if the
// `source` is `null` for whatever reason. It must be `undefined`.
if (method == "getCompletionEntryDetails") {
args[4] ??= undefined;
}
try {
return respond(id, ls[method](...args));
} catch (e) {
if (!isCancellationError(e)) {
return respond(
id,
null,
formatErrorWithArgs(e, [id, method, args, scope, maybeChange]),
);
}
return respond(id);
}
}
throw new TypeError(
// @ts-ignore exhausted case statement sets type to never
`Invalid request method for request: "${method}" (${id})`,
);
}
} finally {
ops.op_exit_span(span, true);
}
}