mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
refactor(lsp): store all the assets in Rust when initializing (#14367)
This commit is contained in:
parent
12f7581ed9
commit
c0e3b6096d
6 changed files with 186 additions and 294 deletions
|
@ -323,14 +323,13 @@ fn is_preferred(
|
|||
|
||||
/// Convert changes returned from a TypeScript quick fix action into edits
|
||||
/// for an LSP CodeAction.
|
||||
pub async fn ts_changes_to_edit(
|
||||
pub fn ts_changes_to_edit(
|
||||
changes: &[tsc::FileTextChanges],
|
||||
language_server: &language_server::Inner,
|
||||
) -> Result<Option<lsp::WorkspaceEdit>, AnyError> {
|
||||
let mut text_document_edits = Vec::new();
|
||||
for change in changes {
|
||||
let text_document_edit =
|
||||
change.to_text_document_edit(language_server).await?;
|
||||
let text_document_edit = change.to_text_document_edit(language_server)?;
|
||||
text_document_edits.push(text_document_edit);
|
||||
}
|
||||
Ok(Some(lsp::WorkspaceEdit {
|
||||
|
@ -539,7 +538,7 @@ impl CodeActionCollection {
|
|||
}
|
||||
|
||||
/// Add a TypeScript code fix action to the code actions collection.
|
||||
pub async fn add_ts_fix_action(
|
||||
pub fn add_ts_fix_action(
|
||||
&mut self,
|
||||
specifier: &ModuleSpecifier,
|
||||
action: &tsc::CodeFixAction,
|
||||
|
@ -561,7 +560,7 @@ impl CodeActionCollection {
|
|||
}
|
||||
let action =
|
||||
fix_ts_import_action(specifier, action, &language_server.documents)?;
|
||||
let edit = ts_changes_to_edit(&action.changes, language_server).await?;
|
||||
let edit = ts_changes_to_edit(&action.changes, language_server)?;
|
||||
let code_action = lsp::CodeAction {
|
||||
title: action.description.clone(),
|
||||
kind: Some(lsp::CodeActionKind::QUICKFIX),
|
||||
|
|
|
@ -241,8 +241,7 @@ async fn resolve_implementation_code_lens(
|
|||
data: CodeLensData,
|
||||
language_server: &language_server::Inner,
|
||||
) -> Result<lsp::CodeLens, AnyError> {
|
||||
let asset_or_doc =
|
||||
language_server.get_cached_asset_or_document(&data.specifier)?;
|
||||
let asset_or_doc = language_server.get_asset_or_document(&data.specifier)?;
|
||||
let line_index = asset_or_doc.line_index();
|
||||
let req = tsc::RequestMethod::GetImplementation((
|
||||
data.specifier.clone(),
|
||||
|
@ -311,7 +310,7 @@ async fn resolve_references_code_lens(
|
|||
language_server: &language_server::Inner,
|
||||
) -> Result<lsp::CodeLens, AnyError> {
|
||||
let asset_or_document =
|
||||
language_server.get_cached_asset_or_document(&data.specifier)?;
|
||||
language_server.get_asset_or_document(&data.specifier)?;
|
||||
let line_index = asset_or_document.line_index();
|
||||
let req = tsc::RequestMethod::GetReferences((
|
||||
data.specifier.clone(),
|
||||
|
@ -328,9 +327,8 @@ async fn resolve_references_code_lens(
|
|||
}
|
||||
let reference_specifier =
|
||||
resolve_url(&reference.document_span.file_name)?;
|
||||
let asset_or_doc = language_server
|
||||
.get_asset_or_document(&reference_specifier)
|
||||
.await?;
|
||||
let asset_or_doc =
|
||||
language_server.get_asset_or_document(&reference_specifier)?;
|
||||
locations.push(
|
||||
reference
|
||||
.to_location(asset_or_doc.line_index(), &language_server.url_map),
|
||||
|
|
|
@ -197,7 +197,7 @@ impl LanguageServer {
|
|||
match params.map(serde_json::from_value) {
|
||||
Some(Ok(params)) => Ok(Some(
|
||||
serde_json::to_value(
|
||||
self.0.lock().await.virtual_text_document(params).await?,
|
||||
self.0.lock().await.virtual_text_document(params)?,
|
||||
)
|
||||
.map_err(|err| {
|
||||
error!(
|
||||
|
@ -261,80 +261,30 @@ impl Inner {
|
|||
}
|
||||
}
|
||||
|
||||
/// Searches assets and open documents which might be performed asynchronously,
|
||||
/// hydrating in memory caches for subsequent requests.
|
||||
pub async fn get_asset_or_document(
|
||||
/// Searches assets and documents for the provided
|
||||
/// specifier erroring if it doesn't exist.
|
||||
pub fn get_asset_or_document(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> LspResult<AssetOrDocument> {
|
||||
self
|
||||
.get_maybe_asset_or_document(specifier)
|
||||
.await?
|
||||
.map_or_else(
|
||||
|| {
|
||||
Err(LspError::invalid_params(format!(
|
||||
"Unable to find asset or document for: {}",
|
||||
specifier
|
||||
)))
|
||||
},
|
||||
Ok,
|
||||
)
|
||||
self.get_maybe_asset_or_document(specifier).map_or_else(
|
||||
|| {
|
||||
Err(LspError::invalid_params(format!(
|
||||
"Unable to find asset or document for: {}",
|
||||
specifier
|
||||
)))
|
||||
},
|
||||
Ok,
|
||||
)
|
||||
}
|
||||
|
||||
/// Searches assets and open documents which might be performed asynchronously,
|
||||
/// hydrating in memory caches for subsequent requests.
|
||||
pub async fn get_maybe_asset_or_document(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> LspResult<Option<AssetOrDocument>> {
|
||||
let mark = self.performance.mark(
|
||||
"get_maybe_asset_or_document",
|
||||
Some(json!({ "specifier": specifier })),
|
||||
);
|
||||
let result = if specifier.scheme() == "asset" {
|
||||
self
|
||||
.assets
|
||||
.get(specifier, || self.snapshot())
|
||||
.await?
|
||||
.map(AssetOrDocument::Asset)
|
||||
} else {
|
||||
self.documents.get(specifier).map(AssetOrDocument::Document)
|
||||
};
|
||||
self.performance.measure(mark);
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Only searches already cached assets and documents. If
|
||||
/// the asset or document cannot be found an error is returned.
|
||||
pub fn get_cached_asset_or_document(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> LspResult<AssetOrDocument> {
|
||||
self
|
||||
.get_maybe_cached_asset_or_document(specifier)
|
||||
.map_or_else(
|
||||
|| {
|
||||
Err(LspError::invalid_params(format!(
|
||||
"An unexpected specifier ({}) was provided.",
|
||||
specifier
|
||||
)))
|
||||
},
|
||||
Ok,
|
||||
)
|
||||
}
|
||||
|
||||
/// Only searches already cached assets and documents. If
|
||||
/// the asset or document cannot be found, `None` is returned.
|
||||
pub fn get_maybe_cached_asset_or_document(
|
||||
/// Searches assets and documents for the provided specifier.
|
||||
pub fn get_maybe_asset_or_document(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Option<AssetOrDocument> {
|
||||
if specifier.scheme() == "asset" {
|
||||
self
|
||||
.assets
|
||||
.get_cached(specifier)
|
||||
.flatten()
|
||||
.map(AssetOrDocument::Asset)
|
||||
self.assets.get(specifier).map(AssetOrDocument::Asset)
|
||||
} else {
|
||||
self.documents.get(specifier).map(AssetOrDocument::Document)
|
||||
}
|
||||
|
@ -348,7 +298,7 @@ impl Inner {
|
|||
"get_navigation_tree",
|
||||
Some(json!({ "specifier": specifier })),
|
||||
);
|
||||
let asset_or_doc = self.get_cached_asset_or_document(specifier)?;
|
||||
let asset_or_doc = self.get_asset_or_document(specifier)?;
|
||||
let navigation_tree =
|
||||
if let Some(navigation_tree) = asset_or_doc.maybe_navigation_tree() {
|
||||
navigation_tree
|
||||
|
@ -826,6 +776,8 @@ impl Inner {
|
|||
self.maybe_config_file.as_ref(),
|
||||
);
|
||||
|
||||
self.assets.intitialize(self.snapshot()).await;
|
||||
|
||||
self.performance.measure(mark);
|
||||
Ok(InitializeResult {
|
||||
capabilities,
|
||||
|
@ -1094,7 +1046,7 @@ impl Inner {
|
|||
}
|
||||
|
||||
let mark = self.performance.mark("document_symbol", Some(¶ms));
|
||||
let asset_or_document = self.get_cached_asset_or_document(&specifier)?;
|
||||
let asset_or_document = self.get_asset_or_document(&specifier)?;
|
||||
let line_index = asset_or_document.line_index();
|
||||
|
||||
let navigation_tree =
|
||||
|
@ -1199,7 +1151,7 @@ impl Inner {
|
|||
}
|
||||
|
||||
let mark = self.performance.mark("hover", Some(¶ms));
|
||||
let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|
||||
let asset_or_doc = self.get_asset_or_document(&specifier)?;
|
||||
let hover = if let Some((_, dep, range)) = asset_or_doc
|
||||
.get_maybe_dependency(¶ms.text_document_position_params.position)
|
||||
{
|
||||
|
@ -1285,7 +1237,7 @@ impl Inner {
|
|||
|
||||
let mark = self.performance.mark("code_action", Some(¶ms));
|
||||
let mut all_actions = CodeActionResponse::new();
|
||||
let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|
||||
let asset_or_doc = self.get_asset_or_document(&specifier)?;
|
||||
let line_index = asset_or_doc.line_index();
|
||||
|
||||
// QuickFix
|
||||
|
@ -1345,7 +1297,6 @@ impl Inner {
|
|||
for action in actions {
|
||||
code_actions
|
||||
.add_ts_fix_action(&specifier, &action, diagnostic, self)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Unable to convert fix: {}", err);
|
||||
LspError::internal_error()
|
||||
|
@ -1485,14 +1436,13 @@ impl Inner {
|
|||
LspError::internal_error()
|
||||
})?
|
||||
} else {
|
||||
combined_code_actions.changes.clone()
|
||||
combined_code_actions.changes
|
||||
};
|
||||
let mut code_action = params.clone();
|
||||
code_action.edit =
|
||||
ts_changes_to_edit(&changes, self).await.map_err(|err| {
|
||||
error!("Unable to convert changes to edits: {}", err);
|
||||
LspError::internal_error()
|
||||
})?;
|
||||
code_action.edit = ts_changes_to_edit(&changes, self).map_err(|err| {
|
||||
error!("Unable to convert changes to edits: {}", err);
|
||||
LspError::internal_error()
|
||||
})?;
|
||||
code_action
|
||||
} else if kind.as_str().starts_with(CodeActionKind::REFACTOR.as_str()) {
|
||||
let snapshot = self.snapshot();
|
||||
|
@ -1502,8 +1452,7 @@ impl Inner {
|
|||
error!("Unable to decode code action data: {}", err);
|
||||
LspError::invalid_params("The CodeAction's data is invalid.")
|
||||
})?;
|
||||
let asset_or_doc =
|
||||
self.get_cached_asset_or_document(&action_data.specifier)?;
|
||||
let asset_or_doc = self.get_asset_or_document(&action_data.specifier)?;
|
||||
let line_index = asset_or_doc.line_index();
|
||||
let start = line_index.offset_tsc(action_data.range.start)?;
|
||||
let length = line_index.offset_tsc(action_data.range.end)? - start;
|
||||
|
@ -1549,7 +1498,7 @@ impl Inner {
|
|||
}
|
||||
|
||||
let mark = self.performance.mark("code_lens", Some(¶ms));
|
||||
let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|
||||
let asset_or_doc = self.get_asset_or_document(&specifier)?;
|
||||
let navigation_tree =
|
||||
self.get_navigation_tree(&specifier).await.map_err(|err| {
|
||||
error!("Error getting code lenses for \"{}\": {}", specifier, err);
|
||||
|
@ -1609,7 +1558,7 @@ impl Inner {
|
|||
}
|
||||
|
||||
let mark = self.performance.mark("document_highlight", Some(¶ms));
|
||||
let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|
||||
let asset_or_doc = self.get_asset_or_document(&specifier)?;
|
||||
let line_index = asset_or_doc.line_index();
|
||||
let files_to_search = vec![specifier.clone()];
|
||||
let req = tsc::RequestMethod::GetDocumentHighlights((
|
||||
|
@ -1653,7 +1602,7 @@ impl Inner {
|
|||
}
|
||||
|
||||
let mark = self.performance.mark("references", Some(¶ms));
|
||||
let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|
||||
let asset_or_doc = self.get_asset_or_document(&specifier)?;
|
||||
let line_index = asset_or_doc.line_index();
|
||||
let req = tsc::RequestMethod::GetReferences((
|
||||
specifier.clone(),
|
||||
|
@ -1680,7 +1629,7 @@ impl Inner {
|
|||
line_index.clone()
|
||||
} else {
|
||||
let asset_or_doc =
|
||||
self.get_asset_or_document(&reference_specifier).await?;
|
||||
self.get_asset_or_document(&reference_specifier)?;
|
||||
asset_or_doc.line_index()
|
||||
};
|
||||
results
|
||||
|
@ -1709,7 +1658,7 @@ impl Inner {
|
|||
}
|
||||
|
||||
let mark = self.performance.mark("goto_definition", Some(¶ms));
|
||||
let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|
||||
let asset_or_doc = self.get_asset_or_document(&specifier)?;
|
||||
let line_index = asset_or_doc.line_index();
|
||||
let req = tsc::RequestMethod::GetDefinition((
|
||||
specifier,
|
||||
|
@ -1748,7 +1697,7 @@ impl Inner {
|
|||
}
|
||||
|
||||
let mark = self.performance.mark("goto_definition", Some(¶ms));
|
||||
let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|
||||
let asset_or_doc = self.get_asset_or_document(&specifier)?;
|
||||
let line_index = asset_or_doc.line_index();
|
||||
let req = tsc::RequestMethod::GetTypeDefinition {
|
||||
specifier,
|
||||
|
@ -1767,8 +1716,7 @@ impl Inner {
|
|||
let response = if let Some(definition_info) = maybe_definition_info {
|
||||
let mut location_links = Vec::new();
|
||||
for info in definition_info {
|
||||
if let Some(link) =
|
||||
info.document_span.to_link(line_index.clone(), self).await
|
||||
if let Some(link) = info.document_span.to_link(line_index.clone(), self)
|
||||
{
|
||||
location_links.push(link);
|
||||
}
|
||||
|
@ -1796,7 +1744,7 @@ impl Inner {
|
|||
}
|
||||
|
||||
let mark = self.performance.mark("completion", Some(¶ms));
|
||||
let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|
||||
let asset_or_doc = self.get_asset_or_document(&specifier)?;
|
||||
// Import specifiers are something wholly internal to Deno, so for
|
||||
// completions, we will use internal logic and if there are completions
|
||||
// for imports, we will return those and not send a message into tsc, where
|
||||
|
@ -1920,7 +1868,7 @@ impl Inner {
|
|||
}
|
||||
|
||||
let mark = self.performance.mark("goto_implementation", Some(¶ms));
|
||||
let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|
||||
let asset_or_doc = self.get_asset_or_document(&specifier)?;
|
||||
let line_index = asset_or_doc.line_index();
|
||||
|
||||
let req = tsc::RequestMethod::GetImplementation((
|
||||
|
@ -1939,9 +1887,7 @@ impl Inner {
|
|||
let result = if let Some(implementations) = maybe_implementations {
|
||||
let mut links = Vec::new();
|
||||
for implementation in implementations {
|
||||
if let Some(link) =
|
||||
implementation.to_link(line_index.clone(), self).await
|
||||
{
|
||||
if let Some(link) = implementation.to_link(line_index.clone(), self) {
|
||||
links.push(link)
|
||||
}
|
||||
}
|
||||
|
@ -1966,7 +1912,7 @@ impl Inner {
|
|||
}
|
||||
|
||||
let mark = self.performance.mark("folding_range", Some(¶ms));
|
||||
let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|
||||
let asset_or_doc = self.get_asset_or_document(&specifier)?;
|
||||
|
||||
let req = tsc::RequestMethod::GetOutliningSpans(specifier.clone());
|
||||
let outlining_spans: Vec<tsc::OutliningSpan> = self
|
||||
|
@ -2010,7 +1956,7 @@ impl Inner {
|
|||
}
|
||||
|
||||
let mark = self.performance.mark("incoming_calls", Some(¶ms));
|
||||
let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|
||||
let asset_or_doc = self.get_asset_or_document(&specifier)?;
|
||||
let line_index = asset_or_doc.line_index();
|
||||
|
||||
let req = tsc::RequestMethod::ProvideCallHierarchyIncomingCalls((
|
||||
|
@ -2033,13 +1979,10 @@ impl Inner {
|
|||
.and_then(|uri| fs_util::specifier_to_file_path(uri).ok());
|
||||
let mut resolved_items = Vec::<CallHierarchyIncomingCall>::new();
|
||||
for item in incoming_calls.iter() {
|
||||
if let Some(resolved) = item
|
||||
.try_resolve_call_hierarchy_incoming_call(
|
||||
self,
|
||||
maybe_root_path_owned.as_deref(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
if let Some(resolved) = item.try_resolve_call_hierarchy_incoming_call(
|
||||
self,
|
||||
maybe_root_path_owned.as_deref(),
|
||||
) {
|
||||
resolved_items.push(resolved);
|
||||
}
|
||||
}
|
||||
|
@ -2059,7 +2002,7 @@ impl Inner {
|
|||
}
|
||||
|
||||
let mark = self.performance.mark("outgoing_calls", Some(¶ms));
|
||||
let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|
||||
let asset_or_doc = self.get_asset_or_document(&specifier)?;
|
||||
let line_index = asset_or_doc.line_index();
|
||||
|
||||
let req = tsc::RequestMethod::ProvideCallHierarchyOutgoingCalls((
|
||||
|
@ -2082,14 +2025,11 @@ impl Inner {
|
|||
.and_then(|uri| fs_util::specifier_to_file_path(uri).ok());
|
||||
let mut resolved_items = Vec::<CallHierarchyOutgoingCall>::new();
|
||||
for item in outgoing_calls.iter() {
|
||||
if let Some(resolved) = item
|
||||
.try_resolve_call_hierarchy_outgoing_call(
|
||||
line_index.clone(),
|
||||
self,
|
||||
maybe_root_path_owned.as_deref(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
if let Some(resolved) = item.try_resolve_call_hierarchy_outgoing_call(
|
||||
line_index.clone(),
|
||||
self,
|
||||
maybe_root_path_owned.as_deref(),
|
||||
) {
|
||||
resolved_items.push(resolved);
|
||||
}
|
||||
}
|
||||
|
@ -2113,7 +2053,7 @@ impl Inner {
|
|||
let mark = self
|
||||
.performance
|
||||
.mark("prepare_call_hierarchy", Some(¶ms));
|
||||
let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|
||||
let asset_or_doc = self.get_asset_or_document(&specifier)?;
|
||||
let line_index = asset_or_doc.line_index();
|
||||
|
||||
let req = tsc::RequestMethod::PrepareCallHierarchy((
|
||||
|
@ -2139,25 +2079,19 @@ impl Inner {
|
|||
let mut resolved_items = Vec::<CallHierarchyItem>::new();
|
||||
match one_or_many {
|
||||
tsc::OneOrMany::One(item) => {
|
||||
if let Some(resolved) = item
|
||||
.try_resolve_call_hierarchy_item(
|
||||
self,
|
||||
maybe_root_path_owned.as_deref(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
if let Some(resolved) = item.try_resolve_call_hierarchy_item(
|
||||
self,
|
||||
maybe_root_path_owned.as_deref(),
|
||||
) {
|
||||
resolved_items.push(resolved)
|
||||
}
|
||||
}
|
||||
tsc::OneOrMany::Many(items) => {
|
||||
for item in items.iter() {
|
||||
if let Some(resolved) = item
|
||||
.try_resolve_call_hierarchy_item(
|
||||
self,
|
||||
maybe_root_path_owned.as_deref(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
if let Some(resolved) = item.try_resolve_call_hierarchy_item(
|
||||
self,
|
||||
maybe_root_path_owned.as_deref(),
|
||||
) {
|
||||
resolved_items.push(resolved);
|
||||
}
|
||||
}
|
||||
|
@ -2185,7 +2119,7 @@ impl Inner {
|
|||
}
|
||||
|
||||
let mark = self.performance.mark("rename", Some(¶ms));
|
||||
let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|
||||
let asset_or_doc = self.get_asset_or_document(&specifier)?;
|
||||
let line_index = asset_or_doc.line_index();
|
||||
|
||||
let req = tsc::RequestMethod::FindRenameLocations {
|
||||
|
@ -2235,7 +2169,7 @@ impl Inner {
|
|||
}
|
||||
|
||||
let mark = self.performance.mark("selection_range", Some(¶ms));
|
||||
let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|
||||
let asset_or_doc = self.get_asset_or_document(&specifier)?;
|
||||
let line_index = asset_or_doc.line_index();
|
||||
|
||||
let mut selection_ranges = Vec::<SelectionRange>::new();
|
||||
|
@ -2273,7 +2207,7 @@ impl Inner {
|
|||
}
|
||||
|
||||
let mark = self.performance.mark("semantic_tokens_full", Some(¶ms));
|
||||
let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|
||||
let asset_or_doc = self.get_asset_or_document(&specifier)?;
|
||||
let line_index = asset_or_doc.line_index();
|
||||
|
||||
let req = tsc::RequestMethod::GetEncodedSemanticClassifications((
|
||||
|
@ -2317,7 +2251,7 @@ impl Inner {
|
|||
let mark = self
|
||||
.performance
|
||||
.mark("semantic_tokens_range", Some(¶ms));
|
||||
let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|
||||
let asset_or_doc = self.get_asset_or_document(&specifier)?;
|
||||
let line_index = asset_or_doc.line_index();
|
||||
|
||||
let start = line_index.offset_tsc(params.range.start)?;
|
||||
|
@ -2360,7 +2294,7 @@ impl Inner {
|
|||
}
|
||||
|
||||
let mark = self.performance.mark("signature_help", Some(¶ms));
|
||||
let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|
||||
let asset_or_doc = self.get_asset_or_document(&specifier)?;
|
||||
let line_index = asset_or_doc.line_index();
|
||||
let options = if let Some(context) = params.context {
|
||||
tsc::SignatureHelpItemsOptions {
|
||||
|
@ -2425,7 +2359,7 @@ impl Inner {
|
|||
} else {
|
||||
let mut symbol_information = Vec::new();
|
||||
for item in navigate_to_items {
|
||||
if let Some(info) = item.to_symbol_information(self).await {
|
||||
if let Some(info) = item.to_symbol_information(self) {
|
||||
symbol_information.push(info);
|
||||
}
|
||||
}
|
||||
|
@ -2918,7 +2852,7 @@ impl Inner {
|
|||
Ok(Some(json!(true)))
|
||||
}
|
||||
|
||||
async fn virtual_text_document(
|
||||
fn virtual_text_document(
|
||||
&mut self,
|
||||
params: lsp_custom::VirtualTextDocumentParams,
|
||||
) -> LspResult<Option<String>> {
|
||||
|
@ -2987,10 +2921,7 @@ impl Inner {
|
|||
}
|
||||
Some(contents)
|
||||
} else {
|
||||
let asset_or_doc = self
|
||||
.get_maybe_asset_or_document(&specifier)
|
||||
.await
|
||||
.map_err(|_| LspError::internal_error())?;
|
||||
let asset_or_doc = self.get_maybe_asset_or_document(&specifier);
|
||||
if let Some(asset_or_doc) = asset_or_doc {
|
||||
Some(asset_or_doc.text().to_string())
|
||||
} else {
|
||||
|
|
234
cli/lsp/tsc.rs
234
cli/lsp/tsc.rs
|
@ -43,7 +43,6 @@ use deno_core::ModuleSpecifier;
|
|||
use deno_core::OpState;
|
||||
use deno_core::RuntimeOptions;
|
||||
use deno_runtime::tokio_util::create_basic_runtime;
|
||||
use log::error;
|
||||
use log::warn;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Captures;
|
||||
|
@ -215,7 +214,7 @@ impl AssetDocument {
|
|||
}
|
||||
}
|
||||
|
||||
type AssetsMap = HashMap<ModuleSpecifier, Option<AssetDocument>>;
|
||||
type AssetsMap = HashMap<ModuleSpecifier, AssetDocument>;
|
||||
|
||||
fn new_assets_map() -> Arc<Mutex<AssetsMap>> {
|
||||
let assets = tsc::STATIC_ASSETS
|
||||
|
@ -224,7 +223,7 @@ fn new_assets_map() -> Arc<Mutex<AssetsMap>> {
|
|||
let url_str = format!("asset:///{}", k);
|
||||
let specifier = resolve_url(&url_str).unwrap();
|
||||
let asset = AssetDocument::new(specifier.clone(), v);
|
||||
(specifier, Some(asset))
|
||||
(specifier, asset)
|
||||
})
|
||||
.collect();
|
||||
Arc::new(Mutex::new(assets))
|
||||
|
@ -245,10 +244,7 @@ impl AssetsSnapshot {
|
|||
self.0.lock().contains_key(k)
|
||||
}
|
||||
|
||||
pub fn get_cached(
|
||||
&self,
|
||||
k: &ModuleSpecifier,
|
||||
) -> Option<Option<AssetDocument>> {
|
||||
pub fn get(&self, k: &ModuleSpecifier) -> Option<AssetDocument> {
|
||||
self.0.lock().get(k).cloned()
|
||||
}
|
||||
}
|
||||
|
@ -269,47 +265,25 @@ impl Assets {
|
|||
}
|
||||
}
|
||||
|
||||
/// Initializes with the assets in the isolate.
|
||||
pub async fn intitialize(&self, state_snapshot: Arc<StateSnapshot>) {
|
||||
let assets = get_isolate_assets(&self.ts_server, state_snapshot).await;
|
||||
let mut assets_map = self.assets.lock();
|
||||
for asset in assets {
|
||||
if !assets_map.contains_key(asset.specifier()) {
|
||||
assets_map.insert(asset.specifier().clone(), asset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn snapshot(&self) -> AssetsSnapshot {
|
||||
// it's ok to not make a complete copy for snapshotting purposes
|
||||
// because assets are static
|
||||
AssetsSnapshot(self.assets.clone())
|
||||
}
|
||||
|
||||
pub fn get_cached(
|
||||
&self,
|
||||
k: &ModuleSpecifier,
|
||||
) -> Option<Option<AssetDocument>> {
|
||||
self.assets.lock().get(k).cloned()
|
||||
}
|
||||
|
||||
pub async fn get(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
// todo(dsherret): this shouldn't be a parameter, but instead retrieved via
|
||||
// a constructor dependency
|
||||
get_snapshot: impl Fn() -> Arc<StateSnapshot>,
|
||||
) -> LspResult<Option<AssetDocument>> {
|
||||
// Race conditions are ok to happen here since the assets are static
|
||||
if let Some(maybe_asset) = self.get_cached(specifier) {
|
||||
Ok(maybe_asset)
|
||||
} else {
|
||||
let maybe_asset = get_asset(specifier, &self.ts_server, get_snapshot())
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error getting asset {}: {}", specifier, err);
|
||||
LspError::internal_error()
|
||||
})?;
|
||||
// if another thread has inserted into the cache, return the asset
|
||||
// that already exists in the cache so that we don't store duplicate
|
||||
// assets in memory anywhere
|
||||
let mut assets = self.assets.lock();
|
||||
if let Some(maybe_asset) = assets.get(specifier) {
|
||||
Ok(maybe_asset.clone())
|
||||
} else {
|
||||
assets.insert(specifier.clone(), maybe_asset.clone());
|
||||
Ok(maybe_asset)
|
||||
}
|
||||
}
|
||||
pub fn get(&self, specifier: &ModuleSpecifier) -> Option<AssetDocument> {
|
||||
self.assets.lock().get(specifier).cloned()
|
||||
}
|
||||
|
||||
pub fn cache_navigation_tree(
|
||||
|
@ -318,38 +292,44 @@ impl Assets {
|
|||
navigation_tree: Arc<NavigationTree>,
|
||||
) -> Result<(), AnyError> {
|
||||
let mut assets = self.assets.lock();
|
||||
let maybe_doc = assets
|
||||
let doc = assets
|
||||
.get_mut(specifier)
|
||||
.ok_or_else(|| anyhow!("Missing asset."))?;
|
||||
let doc = maybe_doc
|
||||
.as_mut()
|
||||
.ok_or_else(|| anyhow!("Cannot get doc mutable"))?;
|
||||
*doc = doc.with_navigation_tree(navigation_tree);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Optionally returns an internal asset, first checking for any static assets
|
||||
/// in Rust, then checking any previously retrieved static assets from the
|
||||
/// isolate, and then finally, the tsc isolate itself.
|
||||
async fn get_asset(
|
||||
specifier: &ModuleSpecifier,
|
||||
/// Get all the assets stored in the tsc isolate.
|
||||
async fn get_isolate_assets(
|
||||
ts_server: &TsServer,
|
||||
state_snapshot: Arc<StateSnapshot>,
|
||||
) -> Result<Option<AssetDocument>, AnyError> {
|
||||
let specifier_str = specifier.to_string().replace("asset:///", "");
|
||||
if let Some(text) = tsc::get_asset(&specifier_str) {
|
||||
let maybe_asset = Some(AssetDocument::new(specifier.clone(), text));
|
||||
Ok(maybe_asset)
|
||||
} else {
|
||||
let res = ts_server
|
||||
.request(state_snapshot, RequestMethod::GetAsset(specifier.clone()))
|
||||
.await?;
|
||||
let maybe_text: Option<String> = serde_json::from_value(res)?;
|
||||
let maybe_asset =
|
||||
maybe_text.map(|text| AssetDocument::new(specifier.clone(), text));
|
||||
Ok(maybe_asset)
|
||||
) -> Vec<AssetDocument> {
|
||||
let res: Value = ts_server
|
||||
.request(state_snapshot, RequestMethod::GetAssets)
|
||||
.await
|
||||
.unwrap();
|
||||
let response_assets = match res {
|
||||
Value::Array(value) => value,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let mut assets = Vec::with_capacity(response_assets.len());
|
||||
|
||||
for asset in response_assets {
|
||||
let mut obj = match asset {
|
||||
Value::Object(obj) => obj,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let specifier_str = obj.get("specifier").unwrap().as_str().unwrap();
|
||||
let specifier = ModuleSpecifier::parse(specifier_str).unwrap();
|
||||
let text = match obj.remove("text").unwrap() {
|
||||
Value::String(text) => text,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
assets.push(AssetDocument::new(specifier, text));
|
||||
}
|
||||
|
||||
assets
|
||||
}
|
||||
|
||||
fn get_tag_body_text(
|
||||
|
@ -829,16 +809,14 @@ pub struct DocumentSpan {
|
|||
}
|
||||
|
||||
impl DocumentSpan {
|
||||
pub async fn to_link(
|
||||
pub fn to_link(
|
||||
&self,
|
||||
line_index: Arc<LineIndex>,
|
||||
language_server: &language_server::Inner,
|
||||
) -> Option<lsp::LocationLink> {
|
||||
let target_specifier = normalize_specifier(&self.file_name).ok()?;
|
||||
let target_asset_or_doc = language_server
|
||||
.get_asset_or_document(&target_specifier)
|
||||
.await
|
||||
.ok()?;
|
||||
let target_asset_or_doc =
|
||||
language_server.get_maybe_asset_or_document(&target_specifier)?;
|
||||
let target_line_index = target_asset_or_doc.line_index();
|
||||
let target_uri = language_server
|
||||
.url_map
|
||||
|
@ -883,7 +861,7 @@ impl DocumentSpan {
|
|||
) -> Option<ModuleSpecifier> {
|
||||
let specifier = normalize_specifier(&self.file_name).ok()?;
|
||||
let asset_or_doc =
|
||||
language_server.get_maybe_cached_asset_or_document(&specifier)?;
|
||||
language_server.get_maybe_asset_or_document(&specifier)?;
|
||||
let line_index = asset_or_doc.line_index();
|
||||
let range = self.text_span.to_range(line_index);
|
||||
let mut target = language_server
|
||||
|
@ -927,15 +905,13 @@ pub struct NavigateToItem {
|
|||
}
|
||||
|
||||
impl NavigateToItem {
|
||||
pub async fn to_symbol_information(
|
||||
pub fn to_symbol_information(
|
||||
&self,
|
||||
language_server: &mut language_server::Inner,
|
||||
) -> Option<lsp::SymbolInformation> {
|
||||
let specifier = normalize_specifier(&self.file_name).ok()?;
|
||||
let asset_or_doc = language_server
|
||||
.get_asset_or_document(&specifier)
|
||||
.await
|
||||
.ok()?;
|
||||
let asset_or_doc =
|
||||
language_server.get_asset_or_document(&specifier).ok()?;
|
||||
let line_index = asset_or_doc.line_index();
|
||||
let uri = language_server
|
||||
.url_map
|
||||
|
@ -1148,15 +1124,12 @@ impl ImplementationLocation {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn to_link(
|
||||
pub fn to_link(
|
||||
&self,
|
||||
line_index: Arc<LineIndex>,
|
||||
language_server: &language_server::Inner,
|
||||
) -> Option<lsp::LocationLink> {
|
||||
self
|
||||
.document_span
|
||||
.to_link(line_index, language_server)
|
||||
.await
|
||||
self.document_span.to_link(line_index, language_server)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1185,8 +1158,7 @@ impl RenameLocations {
|
|||
for location in self.locations.iter() {
|
||||
let specifier = normalize_specifier(&location.document_span.file_name)?;
|
||||
let uri = language_server.url_map.normalize_specifier(&specifier)?;
|
||||
let asset_or_doc =
|
||||
language_server.get_asset_or_document(&specifier).await?;
|
||||
let asset_or_doc = language_server.get_asset_or_document(&specifier)?;
|
||||
|
||||
// ensure TextDocumentEdit for `location.file_name`.
|
||||
if text_document_edit_map.get(&uri).is_none() {
|
||||
|
@ -1276,7 +1248,6 @@ impl DefinitionInfoAndBoundSpan {
|
|||
if let Some(link) = di
|
||||
.document_span
|
||||
.to_link(line_index.clone(), language_server)
|
||||
.await
|
||||
{
|
||||
location_links.push(link);
|
||||
}
|
||||
|
@ -1345,13 +1316,12 @@ pub struct FileTextChanges {
|
|||
}
|
||||
|
||||
impl FileTextChanges {
|
||||
pub async fn to_text_document_edit(
|
||||
pub fn to_text_document_edit(
|
||||
&self,
|
||||
language_server: &language_server::Inner,
|
||||
) -> Result<lsp::TextDocumentEdit, AnyError> {
|
||||
let specifier = normalize_specifier(&self.file_name)?;
|
||||
let asset_or_doc =
|
||||
language_server.get_asset_or_document(&specifier).await?;
|
||||
let asset_or_doc = language_server.get_asset_or_document(&specifier)?;
|
||||
let edits = self
|
||||
.text_changes
|
||||
.iter()
|
||||
|
@ -1359,22 +1329,21 @@ impl FileTextChanges {
|
|||
.collect();
|
||||
Ok(lsp::TextDocumentEdit {
|
||||
text_document: lsp::OptionalVersionedTextDocumentIdentifier {
|
||||
uri: specifier.clone(),
|
||||
uri: specifier,
|
||||
version: asset_or_doc.document_lsp_version(),
|
||||
},
|
||||
edits,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn to_text_document_change_ops(
|
||||
pub fn to_text_document_change_ops(
|
||||
&self,
|
||||
language_server: &language_server::Inner,
|
||||
) -> Result<Vec<lsp::DocumentChangeOperation>, AnyError> {
|
||||
let mut ops = Vec::<lsp::DocumentChangeOperation>::new();
|
||||
let specifier = normalize_specifier(&self.file_name)?;
|
||||
let maybe_asset_or_document = if !self.is_new_file.unwrap_or(false) {
|
||||
let asset_or_doc =
|
||||
language_server.get_asset_or_document(&specifier).await?;
|
||||
let asset_or_doc = language_server.get_asset_or_document(&specifier)?;
|
||||
Some(asset_or_doc)
|
||||
} else {
|
||||
None
|
||||
|
@ -1404,7 +1373,7 @@ impl FileTextChanges {
|
|||
.collect();
|
||||
ops.push(lsp::DocumentChangeOperation::Edit(lsp::TextDocumentEdit {
|
||||
text_document: lsp::OptionalVersionedTextDocumentIdentifier {
|
||||
uri: specifier.clone(),
|
||||
uri: specifier,
|
||||
version: maybe_asset_or_document.and_then(|d| d.document_lsp_version()),
|
||||
},
|
||||
edits,
|
||||
|
@ -1609,7 +1578,7 @@ impl RefactorEditInfo {
|
|||
) -> Result<Option<lsp::WorkspaceEdit>, AnyError> {
|
||||
let mut all_ops = Vec::<lsp::DocumentChangeOperation>::new();
|
||||
for edit in self.edits.iter() {
|
||||
let ops = edit.to_text_document_change_ops(language_server).await?;
|
||||
let ops = edit.to_text_document_change_ops(language_server)?;
|
||||
all_ops.extend(ops);
|
||||
}
|
||||
|
||||
|
@ -1700,16 +1669,14 @@ pub struct CallHierarchyItem {
|
|||
}
|
||||
|
||||
impl CallHierarchyItem {
|
||||
pub async fn try_resolve_call_hierarchy_item(
|
||||
pub fn try_resolve_call_hierarchy_item(
|
||||
&self,
|
||||
language_server: &language_server::Inner,
|
||||
maybe_root_path: Option<&Path>,
|
||||
) -> Option<lsp::CallHierarchyItem> {
|
||||
let target_specifier = normalize_specifier(&self.file).ok()?;
|
||||
let target_asset_or_doc = language_server
|
||||
.get_asset_or_document(&target_specifier)
|
||||
.await
|
||||
.ok()?;
|
||||
let target_asset_or_doc =
|
||||
language_server.get_maybe_asset_or_document(&target_specifier)?;
|
||||
|
||||
Some(self.to_call_hierarchy_item(
|
||||
target_asset_or_doc.line_index(),
|
||||
|
@ -1801,16 +1768,14 @@ pub struct CallHierarchyIncomingCall {
|
|||
}
|
||||
|
||||
impl CallHierarchyIncomingCall {
|
||||
pub async fn try_resolve_call_hierarchy_incoming_call(
|
||||
pub fn try_resolve_call_hierarchy_incoming_call(
|
||||
&self,
|
||||
language_server: &language_server::Inner,
|
||||
maybe_root_path: Option<&Path>,
|
||||
) -> Option<lsp::CallHierarchyIncomingCall> {
|
||||
let target_specifier = normalize_specifier(&self.from.file).ok()?;
|
||||
let target_asset_or_doc = language_server
|
||||
.get_asset_or_document(&target_specifier)
|
||||
.await
|
||||
.ok()?;
|
||||
let target_asset_or_doc =
|
||||
language_server.get_maybe_asset_or_document(&target_specifier)?;
|
||||
|
||||
Some(lsp::CallHierarchyIncomingCall {
|
||||
from: self.from.to_call_hierarchy_item(
|
||||
|
@ -1835,17 +1800,15 @@ pub struct CallHierarchyOutgoingCall {
|
|||
}
|
||||
|
||||
impl CallHierarchyOutgoingCall {
|
||||
pub async fn try_resolve_call_hierarchy_outgoing_call(
|
||||
pub fn try_resolve_call_hierarchy_outgoing_call(
|
||||
&self,
|
||||
line_index: Arc<LineIndex>,
|
||||
language_server: &language_server::Inner,
|
||||
maybe_root_path: Option<&Path>,
|
||||
) -> Option<lsp::CallHierarchyOutgoingCall> {
|
||||
let target_specifier = normalize_specifier(&self.to.file).ok()?;
|
||||
let target_asset_or_doc = language_server
|
||||
.get_asset_or_document(&target_specifier)
|
||||
.await
|
||||
.ok()?;
|
||||
let target_asset_or_doc =
|
||||
language_server.get_maybe_asset_or_document(&target_specifier)?;
|
||||
|
||||
Some(lsp::CallHierarchyOutgoingCall {
|
||||
to: self.to.to_call_hierarchy_item(
|
||||
|
@ -2612,9 +2575,7 @@ fn op_get_length(
|
|||
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(Some(asset)) =
|
||||
state.state_snapshot.assets.get_cached(&specifier)
|
||||
{
|
||||
let r = if let Some(asset) = state.state_snapshot.assets.get(&specifier) {
|
||||
Ok(asset.length())
|
||||
} else {
|
||||
cache_snapshot(state, &specifier, args.version.clone())?;
|
||||
|
@ -2645,8 +2606,8 @@ fn op_get_text(
|
|||
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_cached(&specifier);
|
||||
let content = if let Some(Some(content)) = &maybe_asset {
|
||||
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())?;
|
||||
|
@ -2978,8 +2939,7 @@ pub enum RequestMethod {
|
|||
find_in_comments: bool,
|
||||
provide_prefix_and_suffix_text_for_rename: bool,
|
||||
},
|
||||
/// Retrieve the text of an assets that exists in memory in the isolate.
|
||||
GetAsset(ModuleSpecifier),
|
||||
GetAssets,
|
||||
/// Retrieve the possible refactor info for a range of a file.
|
||||
GetApplicableRefactors((ModuleSpecifier, TextSpan, String)),
|
||||
/// Retrieve the refactor edit info for a range.
|
||||
|
@ -3060,10 +3020,9 @@ impl RequestMethod {
|
|||
"providePrefixAndSuffixTextForRename": provide_prefix_and_suffix_text_for_rename
|
||||
})
|
||||
}
|
||||
RequestMethod::GetAsset(specifier) => json!({
|
||||
RequestMethod::GetAssets => json!({
|
||||
"id": id,
|
||||
"method": "getAsset",
|
||||
"specifier": specifier,
|
||||
"method": "getAssets",
|
||||
}),
|
||||
RequestMethod::GetApplicableRefactors((specifier, span, kind)) => json!({
|
||||
"id": id,
|
||||
|
@ -3743,31 +3702,32 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_request_asset() {
|
||||
fn test_request_assets() {
|
||||
let temp_dir = TempDir::new();
|
||||
let (mut runtime, state_snapshot, _) = setup(
|
||||
&temp_dir,
|
||||
false,
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"noEmit": true,
|
||||
}),
|
||||
&[],
|
||||
);
|
||||
let specifier =
|
||||
resolve_url("asset:///lib.esnext.d.ts").expect("could not resolve url");
|
||||
let (mut runtime, state_snapshot, _) =
|
||||
setup(&temp_dir, false, json!({}), &[]);
|
||||
let result = request(
|
||||
&mut runtime,
|
||||
state_snapshot,
|
||||
RequestMethod::GetAsset(specifier),
|
||||
RequestMethod::GetAssets,
|
||||
Default::default(),
|
||||
);
|
||||
assert!(result.is_ok());
|
||||
let response: Option<String> =
|
||||
serde_json::from_value(result.unwrap()).unwrap();
|
||||
assert!(response.is_some());
|
||||
)
|
||||
.unwrap();
|
||||
let assets = result.as_array().unwrap();
|
||||
|
||||
// You might have found this assertion starts failing after upgrading TypeScript.
|
||||
// Just update the new number of assets (declaration files) for this number.
|
||||
assert_eq!(assets.len(), 66);
|
||||
|
||||
// get some notification when the size of the assets grows
|
||||
let mut total_size = 0;
|
||||
for asset in assets {
|
||||
let obj = asset.as_object().unwrap();
|
||||
let text = obj.get("text").unwrap().as_str().unwrap();
|
||||
total_size += text.len();
|
||||
}
|
||||
assert!(total_size > 0);
|
||||
assert!(total_size < 2_000_000); // currently as of TS 4.6, it's 0.7MB
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -611,12 +611,17 @@ delete Object.prototype.__proto__;
|
|||
),
|
||||
);
|
||||
}
|
||||
case "getAsset": {
|
||||
const sourceFile = host.getSourceFile(
|
||||
request.specifier,
|
||||
ts.ScriptTarget.ESNext,
|
||||
);
|
||||
return respond(id, sourceFile && sourceFile.text);
|
||||
case "getAssets": {
|
||||
const assets = [];
|
||||
for (const sourceFile of sourceFileCache.values()) {
|
||||
if (sourceFile.fileName.startsWith(ASSETS)) {
|
||||
assets.push({
|
||||
specifier: sourceFile.fileName,
|
||||
text: sourceFile.text,
|
||||
});
|
||||
}
|
||||
}
|
||||
return respond(id, assets);
|
||||
}
|
||||
case "getApplicableRefactors": {
|
||||
return respond(
|
||||
|
|
7
cli/tsc/compiler.d.ts
vendored
7
cli/tsc/compiler.d.ts
vendored
|
@ -46,7 +46,7 @@ declare global {
|
|||
type LanguageServerRequest =
|
||||
| ConfigureRequest
|
||||
| FindRenameLocationsRequest
|
||||
| GetAsset
|
||||
| GetAssets
|
||||
| GetApplicableRefactors
|
||||
| GetEditsForRefactor
|
||||
| GetCodeFixes
|
||||
|
@ -91,9 +91,8 @@ declare global {
|
|||
providePrefixAndSuffixTextForRename: boolean;
|
||||
}
|
||||
|
||||
interface GetAsset extends BaseLanguageServerRequest {
|
||||
method: "getAsset";
|
||||
specifier: string;
|
||||
interface GetAssets extends BaseLanguageServerRequest {
|
||||
method: "getAssets";
|
||||
}
|
||||
|
||||
interface GetApplicableRefactors extends BaseLanguageServerRequest {
|
||||
|
|
Loading…
Reference in a new issue