Skip to content

Commit

Permalink
feat: add widget and action tag complete/gotodefs
Browse files Browse the repository at this point in the history
  • Loading branch information
Desdaemon committed Nov 7, 2024
1 parent ce643c7 commit 92e0c16
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 3 deletions.
52 changes: 52 additions & 0 deletions src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,50 @@ impl Backend {
items.extend(completions);
Ok(())
}
pub fn complete_widget(
&self,
range: ByteRange,
rope: Rope,
items: &mut MaxVec<CompletionItem>,
) -> miette::Result<()> {
let range = offset_range_to_lsp_range(range, rope).ok_or_else(|| diagnostic!("(complete_widget) range"))?;
let completions = self.index.widgets.iter().flat_map(|widget| {
let widget = widget.key().to_string();
Some(CompletionItem {
text_edit: Some(CompletionTextEdit::Edit(TextEdit {
range,
new_text: widget.clone(),
})),
label: widget,
kind: Some(CompletionItemKind::ENUM),
..Default::default()
})
});
items.extend(completions);
Ok(())
}
pub fn complete_action_tag(
&self,
range: ByteRange,
rope: Rope,
items: &mut MaxVec<CompletionItem>,
) -> miette::Result<()> {
let range = offset_range_to_lsp_range(range, rope).ok_or_else(|| diagnostic!("(complete_action_tag) range"))?;
let completions = self.index.actions.iter().flat_map(|tag| {
let tag = tag.key().to_string();
Some(CompletionItem {
text_edit: Some(CompletionTextEdit::Edit(TextEdit {
range,
new_text: tag.clone(),
})),
label: tag,
kind: Some(CompletionItemKind::ENUM),
..Default::default()
})
});
items.extend(completions);
Ok(())
}
pub fn jump_def_xml_id(&self, cursor_value: &str, uri: &Url) -> miette::Result<Option<Location>> {
let mut value = Cow::from(cursor_value);
let path = some!(uri.to_file_path().ok());
Expand Down Expand Up @@ -583,6 +627,14 @@ impl Backend {
let prop = some!(self.find_prop_recursive(&component.into(), &prop.into()));
Ok(Some(prop.location.into()))
}
pub fn jump_def_widget(&self, widget: &str) -> miette::Result<Option<Location>> {
let field = some!(self.index.widgets.get(widget.as_bytes()));
Ok(Some(field.value().clone().into()))
}
pub fn jump_def_action_tag(&self, tag: &str) -> miette::Result<Option<Location>> {
let field = some!(self.index.actions.get(tag.as_bytes()));
Ok(Some(field.value().clone().into()))
}
fn find_prop_recursive(&self, component: &Symbol<Component>, prop: &Symbol<Prop>) -> Option<PropDescriptor> {
let component = self.index.components.get(component)?;
if let Some(prop) = component.props.get(prop) {
Expand Down
28 changes: 27 additions & 1 deletion src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use xmlparser::{Token, Tokenizer};

use crate::model::{Model, ModelIndex, ModelType};
use crate::record::Record;
use crate::utils::{path_contains, ts_range_to_lsp_range, ByteOffset, ByteRange, RangeExt, Usage};
use crate::utils::{path_contains, ts_range_to_lsp_range, ByteOffset, ByteRange, MinLoc, RangeExt, Usage};
use crate::{format_loc, ok, ImStr};

mod record;
Expand All @@ -34,6 +34,7 @@ pub use template::TemplateIndex;
mod component;
pub use crate::component::{Component, ComponentName};
pub use component::ComponentQuery;
mod registry;

use crate::template::{gather_templates, NewTemplate};

Expand Down Expand Up @@ -105,6 +106,10 @@ pub struct Index {
pub templates: template::TemplateIndex,
pub models: ModelIndex,
pub components: component::ComponentIndex,
#[default(_code = "DashMap::with_shard_amount(4)")]
pub widgets: DashMap<ImStr, MinLoc>,
#[default(_code = "DashMap::with_shard_amount(4)")]
pub actions: DashMap<ImStr, MinLoc>,
}

pub type ModuleName = Symbol<Module>;
Expand All @@ -121,6 +126,10 @@ enum Output {
models: Vec<Model>,
},
Components(HashMap<ComponentName, Component>),
Registries {
widgets: Vec<(ImStr, MinLoc)>,
actions: Vec<(ImStr, MinLoc)>,
},
}

#[derive(Debug)]
Expand Down Expand Up @@ -313,6 +322,7 @@ impl Index {
for js in scripts {
let Ok(js) = js else { continue };
let path = js.path().to_path_buf();
outputs.spawn(registry::add_root_js(root_key, path.clone()));
outputs.spawn(component::add_root_js(root_key, path));
}
}
Expand Down Expand Up @@ -348,6 +358,18 @@ impl Index {
component_count += components.len();
self.components.extend(components);
}
Output::Registries { widgets, actions } => {
for (widget, loc) in widgets {
if !self.widgets.contains_key(&widget) {
self.widgets.insert(widget, loc);
}
}
for (action, loc) in actions {
if !self.actions.contains_key(&action) {
self.actions.insert(action, loc);
}
}
}
}
}
Ok(Some(AddRootResults {
Expand Down Expand Up @@ -621,6 +643,8 @@ impl Index {
templates,
models,
components,
widgets,
actions,
} = self;
let mut modules = roots.usage();
modules.0 = roots.iter().map(|entry| entry.value().len()).sum::<usize>();
Expand All @@ -630,6 +654,8 @@ impl Index {
"templates": templates.statistics(),
"models": models.statistics(),
"components": components.statistics(),
"widgets": widgets.usage(),
"actions": actions.usage(),
}}
}
}
Expand Down
71 changes: 71 additions & 0 deletions src/index/registry.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use std::path::PathBuf;

use lasso::Spur;
use miette::diagnostic;
use tree_sitter::{Parser, QueryCursor};
use ts_macros::query;

use crate::index::PathSymbol;
use crate::utils::{ts_range_to_lsp_range, MinLoc, RangeExt};
use crate::{format_loc, ok, ImStr};

use super::Output;

#[rustfmt::skip]
query! {
#[lang = "tree_sitter_javascript::language"]
RegistryQuery(Category, Field);

// registry.category(CATEGORY).add(FIELD, ..)
(call_expression
(member_expression
(call_expression
(member_expression (identifier) @_registry (property_identifier) @_category
(#eq? @_registry "registry")
(#eq? @_category "category"))
(arguments . (string) @CATEGORY .))
(property_identifier) @_add (#eq? @_add "add"))
(arguments . (string) @FIELD))
}

pub(super) async fn add_root_js(root: Spur, path: PathBuf) -> miette::Result<Output> {
let contents = ok!(tokio::fs::read(&path).await, "Could not read {:?}", path);
let path = PathSymbol::strip_root(root, &path);
let mut parser = Parser::new();
ok!(parser.set_language(tree_sitter_javascript::language()));
let ast = parser
.parse(&contents, None)
.ok_or_else(|| diagnostic!("AST not parsed"))?;
let query = RegistryQuery::query();
let mut cursor = QueryCursor::new();
let mut widgets = Vec::new();
let mut actions = Vec::new();

for match_ in cursor.matches(query, ast.root_node(), contents.as_slice()) {
let mut category = None;
for capture in match_.captures {
// always a string
let range = capture.node.byte_range().shrink(1);
match RegistryQuery::from(capture.index) {
Some(RegistryQuery::Category) => {
category = Some(&contents[range]);
}
Some(RegistryQuery::Field) => {
let field = String::from_utf8_lossy(&contents[range]);
let loc = MinLoc {
path,
range: ts_range_to_lsp_range(capture.node.range()),
};
match category {
Some(b"fields") => widgets.push((ImStr::from(field.as_ref()), loc)),
Some(b"actions") => actions.push((ImStr::from(field.as_ref()), loc)),
_ => {}
}
}
None => {}
}
}
}

Ok(Output::Registries { widgets, actions })
}
2 changes: 1 addition & 1 deletion src/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ impl Ord for ImStr {
impl std::hash::Hash for ImStr {
#[inline]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.deref().hash(state)
self.as_bytes().hash(state)
}
}

Expand Down
32 changes: 31 additions & 1 deletion src/xml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ enum RefKind<'a> {
PyExpr(usize),
/// `<Component />`
Component,
/// `<field widget=".."/>`
Widget,
/// `<field name="tag">..</field>`
ActionTag,
}

enum Tag<'a> {
Expand Down Expand Up @@ -331,6 +335,12 @@ impl Backend {
&mut items,
)?;
}
RefKind::Widget => {
self.complete_widget(/*needle/, */ replace_range, rope.clone(), &mut items)?;
}
RefKind::ActionTag => {
self.complete_action_tag(/*needle/, */ replace_range, rope.clone(), &mut items)?;
}
RefKind::TName | RefKind::Component => return Ok(None),
}

Expand Down Expand Up @@ -400,6 +410,8 @@ impl Backend {
};
self.jump_def_template_name(interner().resolve(template))
}
Some(RefKind::Widget) => self.jump_def_widget(needle),
Some(RefKind::ActionTag) => self.jump_def_action_tag(needle),
None => Ok(None),
}
}
Expand Down Expand Up @@ -430,6 +442,8 @@ impl Backend {
| Some(RefKind::FieldName(_))
| Some(RefKind::PropOf(..))
| Some(RefKind::Component)
| Some(RefKind::Widget)
| Some(RefKind::ActionTag)
| None => Ok(None),
}
}
Expand Down Expand Up @@ -568,7 +582,7 @@ impl Backend {
})),
}))
}
Some(RefKind::TName) | None => {
Some(RefKind::TName) | Some(RefKind::Widget) | Some(RefKind::ActionTag) | None => {
#[cfg(not(debug_assertions))]
return Ok(None);

Expand Down Expand Up @@ -631,6 +645,7 @@ impl Backend {
let mut depth = 0;
let mut expect_model_string = false;
let mut expect_template_string = false;
let mut expect_action_tag = false;

let mut scope = Scope::default();
let mut parser = Parser::new();
Expand Down Expand Up @@ -697,6 +712,10 @@ impl Backend {
// string reference to a template (ir.ui.view)
expect_template_string = true;
}
"name" if value.as_str() == "tag" => {
// ir.actions tag for a client action (on the `actions` registry)
expect_action_tag = true;
}
"name" if value.as_str() == "arch" => {
arch_mode = true;
arch_depth = depth
Expand Down Expand Up @@ -725,6 +744,10 @@ impl Backend {
arch_model = None;
determine_csv_xmlid_subgroup(&mut ref_at_cursor, value, offset_at_cursor);
}
"widget" if value_in_range && arch_depth > 0 => {
ref_kind = Some(RefKind::Widget);
ref_at_cursor = Some((value.as_str(), value.range()));
}
_ => {}
}
}
Expand Down Expand Up @@ -869,6 +892,13 @@ impl Backend {
model_filter = Some("ir.ui.view".to_string());
}
}
Ok(Token::Text { text }) if expect_action_tag => {
expect_action_tag = false;
if text.range().contains_end(offset_at_cursor) {
ref_at_cursor = Some((text.as_str(), text.range()));
ref_kind = Some(RefKind::ActionTag);
}
}
Ok(Token::ElementEnd { end, span }) => {
foreach_as.reset();
set_value.reset();
Expand Down

0 comments on commit 92e0c16

Please sign in to comment.