Skip to content

Commit 51de4d1

Browse files
committed
feat: create fast indices
1 parent 368b79a commit 51de4d1

File tree

10 files changed

+259
-73
lines changed

10 files changed

+259
-73
lines changed

Cargo.lock

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,5 @@ tower = "0.4.13"
2828
pin-project-lite = "0.2.12"
2929
tree-sitter = "0.20.10"
3030
tree-sitter-python = "0.20.4"
31+
qp-trie = "0.8.1"
3132

src/index.rs

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::path::{Path, PathBuf};
22
use std::sync::OnceLock;
33
use std::time::Duration;
44

5-
use dashmap::{DashMap, DashSet};
5+
use dashmap::DashSet;
66
use faststr::FastStr;
77
use globwalk::FileType;
88
use log::{debug, warn};
@@ -18,11 +18,13 @@ use crate::model::{Model, ModelIndex};
1818
use crate::record::Record;
1919
use crate::utils::{offset_range_to_lsp_range, ByteOffset, CharOffset, RangeExt};
2020

21+
mod record;
22+
2123
#[derive(Default)]
2224
pub struct ModuleIndex {
2325
pub roots: DashSet<String>,
2426
pub modules: DashSet<String>,
25-
pub records: DashMap<String, Record>,
27+
pub records: record::RecordIndex,
2628
pub models: ModelIndex,
2729
}
2830

@@ -140,17 +142,8 @@ impl ModuleIndex {
140142
match outputs {
141143
Output::Records(records) => {
142144
record_count += records.len();
143-
for record in records {
144-
let key = record.qualified_id();
145-
if let Some((_, discarded)) = self.records.remove(&key) {
146-
debug!(
147-
"{key}:\n{} -> {}",
148-
discarded.location.uri.path(),
149-
record.location.uri.path()
150-
)
151-
}
152-
self.records.insert(key, record);
153-
}
145+
let mut prefix = self.records.by_prefix.write().await;
146+
self.records.extend_records(Some(&mut prefix), records).await;
154147
}
155148
Output::Models { path, models } => {
156149
model_count += models.len();
@@ -161,7 +154,7 @@ impl ModuleIndex {
161154
continue;
162155
}
163156
};
164-
self.models.extend_models(&uri, models);
157+
self.models.extend_models(&uri, models).await;
165158
}
166159
}
167160
}

src/index/record.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use std::{ops::Deref, sync::Arc};
2+
3+
use dashmap::{mapref::one::Ref, DashMap, DashSet};
4+
use tokio::sync::RwLock;
5+
6+
use crate::{record::Record, utils::PrefixTrie};
7+
8+
#[derive(Default)]
9+
pub struct RecordIndex {
10+
inner: DashMap<String, Record>,
11+
by_model: DashMap<String, DashSet<String>>,
12+
by_inherit_id: DashMap<String, DashSet<String>>,
13+
pub by_prefix: Arc<RwLock<PrefixTrie>>,
14+
}
15+
16+
impl Deref for RecordIndex {
17+
type Target = DashMap<String, Record>;
18+
#[inline]
19+
fn deref(&self) -> &Self::Target {
20+
&self.inner
21+
}
22+
}
23+
24+
impl RecordIndex {
25+
pub async fn insert(&self, qualified_id: String, record: Record, prefix: Option<&mut PrefixTrie>) {
26+
if let Some(model) = &record.model {
27+
self.by_model
28+
.entry(model.to_string())
29+
.or_default()
30+
.insert(qualified_id.to_string());
31+
}
32+
if let Some(inherit_id) = &record.inherit_id {
33+
let inherit_id = match inherit_id {
34+
(Some(module), xml_id) => format!("{module}.{xml_id}"),
35+
(None, xml_id) => format!("{}.{xml_id}", record.module),
36+
};
37+
self.by_inherit_id
38+
.entry(inherit_id)
39+
.or_default()
40+
.insert(qualified_id.to_string());
41+
}
42+
if let Some(prefix) = prefix {
43+
prefix.insert_str(&qualified_id, qualified_id.to_string());
44+
} else {
45+
self.by_prefix
46+
.write()
47+
.await
48+
.insert_str(&qualified_id, qualified_id.to_string());
49+
}
50+
self.inner.insert(qualified_id, record);
51+
}
52+
pub async fn extend_records(&self, prefix: Option<&mut PrefixTrie>, records: impl IntoIterator<Item = Record>) {
53+
if let Some(prefix) = prefix {
54+
for record in records {
55+
self.insert(record.qualified_id(), record, Some(prefix)).await;
56+
}
57+
} else {
58+
let mut prefix = self.by_prefix.write().await;
59+
for record in records {
60+
self.insert(record.qualified_id(), record, Some(&mut prefix)).await;
61+
}
62+
}
63+
}
64+
pub fn by_model(&self, model: &str) -> impl Iterator<Item = Ref<String, Record>> {
65+
self.by_model
66+
.get(model)
67+
.into_iter()
68+
.flat_map(|ids| self.resolve_references(ids))
69+
}
70+
pub fn by_inherit_id(&self, inherit_id: &str) -> impl Iterator<Item = Ref<String, Record>> {
71+
self.by_inherit_id
72+
.get(inherit_id)
73+
.into_iter()
74+
.flat_map(|ids| self.resolve_references(ids))
75+
}
76+
fn resolve_references(&self, ids: Ref<String, DashSet<String>>) -> impl IntoIterator<Item = Ref<String, Record>> {
77+
ids.value()
78+
.iter()
79+
.flat_map(|id| self.get(id.key()).into_iter())
80+
.collect::<Vec<_>>()
81+
}
82+
}

src/main.rs

Lines changed: 54 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use dashmap::{DashMap, DashSet};
88
use globwalk::FileType;
99
use log::{debug, error, info};
1010
use miette::{diagnostic, IntoDiagnostic};
11+
use odoo_lsp::utils::isolate::Isolate;
1112
use ropey::Rope;
1213
use serde_json::Value;
1314
use tower::ServiceBuilder;
@@ -37,6 +38,7 @@ pub struct Backend {
3738
capabilities: Capabilities,
3839
root_setup: AtomicBool,
3940
symbols_limit: AtomicUsize,
41+
isolate: Isolate,
4042
}
4143

4244
#[derive(Debug, Default)]
@@ -135,6 +137,7 @@ impl LanguageServer for Backend {
135137
capabilities: _,
136138
root_setup: _,
137139
symbols_limit: _,
140+
isolate: _,
138141
} = self;
139142
document_map.remove(path);
140143
record_ranges.remove(path);
@@ -408,7 +411,10 @@ impl LanguageServer for Backend {
408411
return Ok(None);
409412
};
410413
if ext == "xml" {
411-
match self.xml_completions(params, document.value().clone()) {
414+
let completions = self.isolate.send_task(|send| {
415+
Box::pin(async { _ = send.send(self.xml_completions(params, document.value().clone()).await) })
416+
});
417+
match completions.await.expect("isolate error") {
412418
Ok(ret) => Ok(ret),
413419
Err(report) => {
414420
self.client
@@ -422,7 +428,15 @@ impl LanguageServer for Backend {
422428
debug!("Bug: did not build AST for {}", uri.path());
423429
return Ok(None);
424430
};
425-
match self.python_completions(params, ast.value().clone(), document.value().clone()) {
431+
let completions = self.isolate.send_task(|send| {
432+
Box::pin(async {
433+
_ = send.send(
434+
self.python_completions(params, ast.value().clone(), document.value().clone())
435+
.await,
436+
);
437+
})
438+
});
439+
match completions.await.expect("isolate error") {
426440
Ok(ret) => Ok(ret),
427441
Err(err) => {
428442
self.client
@@ -475,8 +489,9 @@ impl LanguageServer for Backend {
475489
#[allow(deprecated)]
476490
async fn symbol(&self, params: WorkspaceSymbolParams) -> Result<Option<Vec<SymbolInformation>>> {
477491
let query = &params.query;
478-
let records = self.module_index.records.iter().filter_map(|entry| {
479-
entry.id.contains(query).then(|| SymbolInformation {
492+
let records_by_prefix = self.module_index.records.by_prefix.read().await;
493+
let records = records_by_prefix.iter_prefix(query.as_bytes()).flat_map(|(_, key)| {
494+
self.module_index.records.get(key).map(|entry| SymbolInformation {
480495
name: entry.id.to_string(),
481496
kind: SymbolKind::VARIABLE,
482497
tags: None,
@@ -485,9 +500,10 @@ impl LanguageServer for Backend {
485500
container_name: None,
486501
})
487502
});
488-
let models = self.module_index.models.iter().filter_map(|entry| {
489-
entry.0.as_ref().and_then(|loc| {
490-
entry.key().contains(query).then(|| SymbolInformation {
503+
let models_by_prefix = self.module_index.models.by_prefix.read().await;
504+
let models = models_by_prefix.iter_prefix(query.as_bytes()).flat_map(|(_, key)| {
505+
self.module_index.models.get(key).into_iter().flat_map(|entry| {
506+
entry.0.as_ref().map(|loc| SymbolInformation {
491507
name: entry.key().clone(),
492508
kind: SymbolKind::CONSTANT,
493509
tags: None,
@@ -533,7 +549,8 @@ impl Backend {
533549
(None, Text::Delta(_)) => Err(diagnostic!("No rope and got delta"))?,
534550
};
535551
if matches!(split_uri, Some((_, "xml"))) || matches!(params.language, Some(Language::Xml)) {
536-
self.on_change_xml(&params.text, &params.uri, rope, &mut diagnostics);
552+
self.on_change_xml(&params.text, &params.uri, rope, &mut diagnostics)
553+
.await;
537554
} else if matches!(split_uri, Some((_, "py"))) || matches!(params.language, Some(Language::Python)) {
538555
self.on_change_python(&params.text, &params.uri, rope, params.old_rope)
539556
.await?;
@@ -652,20 +669,18 @@ impl Backend {
652669
items.extend(matches);
653670
Ok(())
654671
}
655-
fn complete_model(
672+
async fn complete_model(
656673
&self,
657674
needle: &str,
658675
range: std::ops::Range<CharOffset>,
659676
rope: Rope,
660677
items: &mut Vec<CompletionItem>,
661678
) -> miette::Result<()> {
662679
let range = char_range_to_lsp_range(range, rope).ok_or_else(|| diagnostic!("(complete_model) range"))?;
663-
let matches = self
664-
.module_index
665-
.models
666-
.iter()
667-
.filter(|entry| entry.key().contains(needle))
668-
.take(Self::LIMIT)
680+
let by_prefix = self.module_index.models.by_prefix.read().await;
681+
let matches = by_prefix
682+
.iter_prefix(needle.as_bytes())
683+
.flat_map(|(_, key)| self.module_index.models.get(key))
669684
.map(|entry| {
670685
let label = entry.key().to_string();
671686
CompletionItem {
@@ -711,37 +726,37 @@ impl Backend {
711726
None => Ok(None),
712727
}
713728
}
714-
fn model_references(&self, cursor_value: &str) -> miette::Result<Option<Vec<Location>>> {
715-
let mut locations = match self.module_index.models.get(cursor_value) {
729+
fn model_references(&self, model: &str) -> miette::Result<Option<Vec<Location>>> {
730+
let mut locations = match self.module_index.models.get(model) {
716731
Some(entry) => entry.1.iter().map(|loc| loc.0.clone()).collect::<Vec<_>>(),
717732
None => vec![],
718733
};
719-
let record_locations = self
720-
.module_index
721-
.records
722-
.iter()
723-
.filter_map(|record| (record.model.as_deref() == Some(cursor_value)).then(|| record.location.clone()));
724-
locations.extend(record_locations);
734+
locations.extend(
735+
self.module_index
736+
.records
737+
.by_model(model)
738+
.map(|record| record.location.clone()),
739+
);
725740
Ok(Some(locations))
726741
}
727742
fn record_references(
728743
&self,
729-
cursor_value: &str,
744+
inherit_id: &str,
730745
current_module: Option<&str>,
731746
) -> miette::Result<Option<Vec<Location>>> {
732-
let cursor_match = cursor_value.split_once('.');
733-
let locations = self.module_index.records.iter().filter_map(|entry| {
734-
match (&entry.module, &entry.inherit_id, cursor_match, current_module) {
735-
(_, Some((Some(lhs_mod), lhs_id)), Some((rhs_mod, rhs_id)), _) => {
736-
lhs_mod == rhs_mod && lhs_id == rhs_id
737-
}
738-
(lhs_mod, Some((None, lhs_id)), Some((rhs_mod, rhs_id)), _) => lhs_mod == rhs_mod && lhs_id == rhs_id,
739-
(_, Some((Some(lhs_mod), xml_id)), None, Some(rhs_mod)) => lhs_mod == rhs_mod && xml_id == cursor_value,
740-
(lhs_mod, Some((None, xml_id)), None, Some(rhs_mod)) => lhs_mod == rhs_mod && xml_id == cursor_value,
741-
_ => false,
742-
}
743-
.then(|| entry.location.clone())
744-
});
747+
let inherit_id = if inherit_id.contains('.') {
748+
Cow::from(inherit_id)
749+
} else if let Some(current_module) = current_module {
750+
Cow::from(format!("{}.{}", current_module, inherit_id))
751+
} else {
752+
debug!("No current module to resolve the XML ID {inherit_id}");
753+
return Ok(None);
754+
};
755+
let locations = self
756+
.module_index
757+
.records
758+
.by_inherit_id(&inherit_id)
759+
.map(|record| record.location.clone());
745760
Ok(Some(locations.collect()))
746761
}
747762
async fn on_change_config(&self, config: Config) {
@@ -791,7 +806,8 @@ async fn main() {
791806
capabilities: Default::default(),
792807
root_setup: Default::default(),
793808
ast_map: DashMap::new(),
794-
symbols_limit: AtomicUsize::new(80),
809+
symbols_limit: AtomicUsize::new(100),
810+
isolate: Isolate::new(),
795811
})
796812
.finish();
797813

0 commit comments

Comments
 (0)