python: Fix instability of Pyright/BasedPyright code completions (#37850)
Pyright sets different `sortText` based on whether a given completion item was recently resolved. This probably lines up with VSCode's way of resolving items, but it's a no-no for us, as it makes completions unstable. Closes #9983 Release Notes: - python: Fixed code completions having arbitrary order when using Pyright/basedpyright
This commit is contained in:
@@ -87,6 +87,18 @@ fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||
vec![server_path.into(), "--stdio".into()]
|
||||
}
|
||||
|
||||
/// Pyright assigns each completion item a `sortText` of the form `XX.YYYY.name`.
|
||||
/// Where `XX` is the sorting category, `YYYY` is based on most recent usage,
|
||||
/// and `name` is the symbol name itself.
|
||||
///
|
||||
/// The problem with it is that Pyright adjusts the sort text based on previous resolutions (items for which we've issued `completion/resolve` call have their sortText adjusted),
|
||||
/// which - long story short - makes completion items list non-stable. Pyright probably relies on VSCode's implementation detail.
|
||||
/// see https://github.com/microsoft/pyright/blob/95ef4e103b9b2f129c9320427e51b73ea7cf78bd/packages/pyright-internal/src/languageService/completionProvider.ts#LL2873
|
||||
fn process_pyright_completions(items: &mut [lsp::CompletionItem]) {
|
||||
for item in items {
|
||||
item.sort_text.take();
|
||||
}
|
||||
}
|
||||
pub struct PythonLspAdapter {
|
||||
node: NodeRuntime,
|
||||
}
|
||||
@@ -232,26 +244,7 @@ impl LspAdapter for PythonLspAdapter {
|
||||
}
|
||||
|
||||
async fn process_completions(&self, items: &mut [lsp::CompletionItem]) {
|
||||
// Pyright assigns each completion item a `sortText` of the form `XX.YYYY.name`.
|
||||
// Where `XX` is the sorting category, `YYYY` is based on most recent usage,
|
||||
// and `name` is the symbol name itself.
|
||||
//
|
||||
// Because the symbol name is included, there generally are not ties when
|
||||
// sorting by the `sortText`, so the symbol's fuzzy match score is not taken
|
||||
// into account. Here, we remove the symbol name from the sortText in order
|
||||
// to allow our own fuzzy score to be used to break ties.
|
||||
//
|
||||
// see https://github.com/microsoft/pyright/blob/95ef4e103b9b2f129c9320427e51b73ea7cf78bd/packages/pyright-internal/src/languageService/completionProvider.ts#LL2873
|
||||
for item in items {
|
||||
let Some(sort_text) = &mut item.sort_text else {
|
||||
continue;
|
||||
};
|
||||
let mut parts = sort_text.split('.');
|
||||
let Some(first) = parts.next() else { continue };
|
||||
let Some(second) = parts.next() else { continue };
|
||||
let Some(_) = parts.next() else { continue };
|
||||
sort_text.replace_range(first.len() + second.len() + 1.., "");
|
||||
}
|
||||
process_pyright_completions(items);
|
||||
}
|
||||
|
||||
async fn label_for_completion(
|
||||
@@ -1490,26 +1483,7 @@ impl LspAdapter for BasedPyrightLspAdapter {
|
||||
}
|
||||
|
||||
async fn process_completions(&self, items: &mut [lsp::CompletionItem]) {
|
||||
// Pyright assigns each completion item a `sortText` of the form `XX.YYYY.name`.
|
||||
// Where `XX` is the sorting category, `YYYY` is based on most recent usage,
|
||||
// and `name` is the symbol name itself.
|
||||
//
|
||||
// Because the symbol name is included, there generally are not ties when
|
||||
// sorting by the `sortText`, so the symbol's fuzzy match score is not taken
|
||||
// into account. Here, we remove the symbol name from the sortText in order
|
||||
// to allow our own fuzzy score to be used to break ties.
|
||||
//
|
||||
// see https://github.com/microsoft/pyright/blob/95ef4e103b9b2f129c9320427e51b73ea7cf78bd/packages/pyright-internal/src/languageService/completionProvider.ts#LL2873
|
||||
for item in items {
|
||||
let Some(sort_text) = &mut item.sort_text else {
|
||||
continue;
|
||||
};
|
||||
let mut parts = sort_text.split('.');
|
||||
let Some(first) = parts.next() else { continue };
|
||||
let Some(second) = parts.next() else { continue };
|
||||
let Some(_) = parts.next() else { continue };
|
||||
sort_text.replace_range(first.len() + second.len() + 1.., "");
|
||||
}
|
||||
process_pyright_completions(items);
|
||||
}
|
||||
|
||||
async fn label_for_completion(
|
||||
|
||||
Reference in New Issue
Block a user