Skip to content

Commit

Permalink
feat: deflayer key hover
Browse files Browse the repository at this point in the history
  • Loading branch information
rszyma committed Dec 25, 2024
1 parent 490a8fb commit c74c05e
Show file tree
Hide file tree
Showing 7 changed files with 626 additions and 134 deletions.
87 changes: 87 additions & 0 deletions kls/src/formatter/defsrc_layout/get_keys.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use super::{parse_into_ext_tree_and_root_span, ExtParseTree};
use crate::{path_to_url, WorkspaceOptions};
use anyhow::{anyhow, Ok};
use lsp_types::{TextDocumentItem, Url};
use std::{collections::BTreeMap, iter, path::PathBuf, str::FromStr};

pub fn get_defsrc_keys(
workspace_options: &WorkspaceOptions,
documents: &BTreeMap<Url, TextDocumentItem>,
file_uri: &Url, // of current file
tree: &ExtParseTree, // of current file
) -> anyhow::Result<Option<Vec<String>>> {
match workspace_options {
WorkspaceOptions::Single { .. } => {
if tree.includes()?.is_empty() {
tree.defsrc_keys()
} else {
// This is an error, because we don't know if those included files
// and current file collectively don't contain >=2 `defsrc` blocks.
// And if that's the case, we don't want to format `deflayers`.
Err(anyhow!("includes are not supported in Single mode"))
}
}
WorkspaceOptions::Workspace {
main_config_file,
root,
} => {
let main_config_file_path = PathBuf::from_str(main_config_file)
.map_err(|e| anyhow!("main_config_file is an invalid path: {}", e))?;
let main_config_file_url = path_to_url(&main_config_file_path, root)
.map_err(|e| anyhow!("failed to convert main_config_file_path to url: {}", e))?;

// Check if currently opened file is the main file.
let main_tree: ExtParseTree = if main_config_file_url == *file_uri {
tree.clone() // TODO: prevent clone
} else {
// Currently opened file is non-main file, it's probably an included file.
let text = &documents
.get(&main_config_file_url)
.map(|doc| &doc.text)
.ok_or_else(|| {
anyhow!(
"included file is not present in the workspace: {}",
file_uri.to_string()
)
})?;

parse_into_ext_tree_and_root_span(text)
.map(|x| x.0)
.map_err(|e| anyhow!("parse_into_ext_tree_and_root_span failed: {}", e.msg))?
};

let includes = main_tree
.includes()
.map_err(|e| anyhow!("workspace [main = {main_config_file_url}]: {e}"))?
.iter()
.map(|path| path_to_url(path, root))
.collect::<anyhow::Result<Vec<_>>>()
.map_err(|e| anyhow!("path_to_url: {e}"))?;

// make sure that all includes collectively contain only 1 defsrc
let mut defsrc_keys = None;
for file_url in includes.iter().chain(iter::once(&main_config_file_url)) {
let doc = documents
.get(file_url)
.ok_or_else(|| anyhow!("document '{file_url}' is not loaded"))?;

let tree = parse_into_ext_tree_and_root_span(&doc.text)
.map(|x| x.0)
.map_err(|e| {
anyhow!(
"parse_into_ext_tree_and_root_span failed for file '{file_uri}': {}",
e.msg
)
})?;

if let Some(layout) = tree
.defsrc_keys()
.map_err(|e| anyhow!("tree.defsrc_keys for '{file_url}' failed: {e}"))?
{
defsrc_keys = Some(layout);
}
}
Ok(defsrc_keys)
}
}
}
60 changes: 60 additions & 0 deletions kls/src/formatter/defsrc_layout/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use crate::log;
use anyhow::anyhow;
use unicode_segmentation::*;

pub mod get_keys;
pub use get_keys::*;
pub mod get_layout;
pub use get_layout::*;

Expand Down Expand Up @@ -193,6 +195,64 @@ impl ExtParseTree {
// Layout no longer needs to be mutable.
Ok(Some(layout))
}

/// Obtains list of keys in defsrc block in given [`ExtParseTree`].
/// * It doesn't search includes.
/// * Returns `Err` if found more than 1 defsrc, or `defsrc` contains a list.
/// * Returns `Ok(None)` if found 0 defsrc blocks.
/// * Returns `Ok(Some)` otherwise.
pub fn defsrc_keys<'a>(&'a self) -> anyhow::Result<Option<Vec<String>>> {
let mut defsrc: Option<&'a NodeList> = None;

for top_level_item in self.0.iter() {
let top_level_list = match &top_level_item.expr {
Expr::Atom(_) => continue,
Expr::List(list) => list,
};

let first_item = match top_level_list.get(0) {
Some(x) => x,
None => continue,
};

let first_atom = match &first_item.expr {
Expr::Atom(x) => x,
Expr::List(_) => continue,
};

if let "defsrc" = first_atom.as_str() {
match defsrc {
Some(_) => {
return Err(anyhow!("multiple `defsrc` definitions in a single file"));
}
None => {
defsrc = Some(top_level_list);
}
}
}
}

let defsrc = match defsrc {
Some(x) => x,
None => {
// defsrc not found in this file, but it may be in another.
return Ok(None);
}
};

let result: Vec<String> = defsrc
.iter()
.skip(1)
.map(|x| {
Ok(match &x.expr {
Expr::List(_) => return Err(anyhow!("found a list in `defsrc`")),
Expr::Atom(x) => x.clone(),
})
})
.collect::<Result<Vec<_>, _>>()?;

Ok(Some(result))
}
}

/// Format metadata for a definition layer node based on specified constraints.
Expand Down
Loading

0 comments on commit c74c05e

Please sign in to comment.