Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
rszyma committed Jan 31, 2024
1 parent 56c69ac commit 5fd0311
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 140 deletions.
108 changes: 108 additions & 0 deletions kls/src/formatter/defsrc_layout/get_layout.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
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_layout(
workspace_options: &WorkspaceOptions,
documents: &BTreeMap<Url, TextDocumentItem>,
tab_size: u32,
file_uri: &Url, // of current file
tree: &ExtParseTree, // of current file
) -> anyhow::Result<Option<Vec<Vec<usize>>>> {
match workspace_options {
WorkspaceOptions::Single => {
if tree.includes()?.is_empty() {
tree.defsrc_layout(tab_size)
} else {
Ok(None)
}
}
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))?;

let main_tree: ExtParseTree = if main_config_file_url == *file_uri {
// currently opened file is the main file
tree.clone() // TODO: prevent clone
} else {
// currently opened file is non-main file, and probably an included file.
let text = &documents
.get(file_uri)
.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_layout = None;
for file_url in includes.iter().chain(iter::once(&main_config_file_url)) {
let text = &documents
.get(file_url)
.expect("document should be cached")
.text;

let tree = parse_into_ext_tree_and_root_span(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_layout(tab_size)
.map_err(|e| anyhow!("tree.defsrc_layout for '{file_url}' failed: {e}"))?
{
defsrc_layout = Some(layout);
}
}
Ok(defsrc_layout)
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn workspace_single_basic_main_config() {
let src = "(defsrc 1 2) (deflayer base 3 4)";
let layout = get_defsrc_layout(
&WorkspaceOptions::Single,
&BTreeMap::new(),
4,
&Url::from_str("file:///main.kbd").unwrap(),
&parse_into_ext_tree_and_root_span(src).unwrap().0,
)
.unwrap()
.ok_or("should be some")
.unwrap();

assert_eq!(layout, vec![vec![3], vec![1]]);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use super::ext_tree::*;
use crate::log;
use anyhow::{anyhow, Ok};
use unicode_segmentation::*;

pub mod get_layout;
pub use get_layout::*;

impl ExtParseTree {
// TODO: maybe don't format if an atom in defsrc/deflayer is too large.
// TODO: respect `tab_size`.
Expand Down Expand Up @@ -78,9 +82,11 @@ impl ExtParseTree {
}

/// Obtains defsrc layout from a given [`ExtParseTree`].
/// Returns None if found 0 defsrc blocks or found more than 1 defsrc.
/// It doesn't search includes.
pub fn defsrc_layout<'a>(&'a self, tab_size: u32) -> Option<Vec<Vec<usize>>> {
/// * 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_layout<'a>(&'a self, tab_size: u32) -> anyhow::Result<Option<Vec<Vec<usize>>>> {
let mut defsrc: Option<&'a NodeList> = None;

for top_level_item in self.0.iter() {
Expand All @@ -102,11 +108,7 @@ impl ExtParseTree {
if let "defsrc" = first_atom.as_str() {
match defsrc {
Some(_) => {
log!(
"Formatting `deflayer`s failed: config file \
contains multiple `defsrc` definitions."
);
return None;
return Err(anyhow!("multiple `defsrc` definitions in a single file"));
}
None => {
defsrc = Some(top_level_list);
Expand All @@ -115,15 +117,12 @@ impl ExtParseTree {
}
}

let defsrc = if let Some(x) = defsrc {
x
} else {
log!(
"Formatting `deflayer`s failed: `defsrc` not found in this file. \
NOTE: includes (or the main file, if this file is non-main) haven't \
been checked, because it's not implemented yet."
);
return None;
let defsrc = match defsrc {
Some(x) => x,
None => {
// defsrc not found in this file, but it may be in another.
return Ok(None);
}
};

// Get number of atoms from `defsrc` now to prevent additional allocations
Expand All @@ -136,11 +135,7 @@ impl ExtParseTree {
// Read the layout from `defsrc`
for (i, defsrc_item) in defsrc.iter().skip(1).enumerate() {
if let Expr::List(_) = defsrc_item.expr {
log!(
"Formatting `deflayer`s failed: there shouldn't \
be any lists in `defsrc`."
);
return None;
return Err(anyhow!("found a list in `defsrc`"));
}

let defsrc_item_as_str = defsrc_item.expr.to_string();
Expand Down Expand Up @@ -186,7 +181,7 @@ impl ExtParseTree {
}

// Layout no longer needs to be mutable.
Some(layout)
Ok(Some(layout))
}
}

Expand Down Expand Up @@ -306,7 +301,7 @@ mod tests {
fn formats_correctly(input: &str, expected_output: &str) {
let mut tree = parse_into_ext_tree(input).expect("parses");
let tab_size = 4;
if let Some(layout) = tree.defsrc_layout(tab_size) {
if let Some(layout) = tree.defsrc_layout(tab_size).expect("no err") {
tree.use_defsrc_layout_on_deflayers(&layout, tab_size, true);
};
assert_eq!(
Expand Down
15 changes: 11 additions & 4 deletions kls/src/formatter/ext_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,6 @@ impl ExtParseTree {
let mut result = vec![];
for top_level_block in self.0.iter() {
if let Expr::List(NodeList::NonEmptyList(xs)) = &top_level_block.expr {
if xs.len() != 2 {
return Err(anyhow!("an include block contains more than 2 items"));
}

match &xs[0].expr {
Expr::Atom(x) => match x.as_str() {
"include" => {}
Expand All @@ -78,6 +74,17 @@ impl ExtParseTree {
_ => continue,
};

if xs.len() != 2 {
return Err(anyhow!(
"an include block items: 2 != {}; block: \n{}",
xs.len(),
xs.iter().fold(String::new(), |mut acc, x| {
acc.push_str(&x.to_string());
acc
})
));
}

if let Expr::Atom(x) = &xs[1].expr {
result.push(PathBuf::from_str(x.as_str().trim_matches('\"'))?)
}
Expand Down
5 changes: 4 additions & 1 deletion kls/src/formatter/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
pub mod ext_tree;
use ext_tree::*;

use crate::log;

pub mod defsrc_layout;
mod remove_excessive_newlines;
mod use_defsrc_layout_on_deflayers;

pub struct Formatter {
// Additional options
Expand Down Expand Up @@ -32,6 +34,7 @@ impl Formatter {
}

if self.options.use_defsrc_layout_on_deflayers {
log!("formatting defsrc - layout: {:?}", defsrc_layout);
if let Some(layout) = defsrc_layout {
tree.use_defsrc_layout_on_deflayers(
layout,
Expand Down
121 changes: 10 additions & 111 deletions kls/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
use crate::{
formatter::ext_tree::ExtParseTree,
helpers::{lsp_range_from_span, HashSet},
};
use crate::helpers::{lsp_range_from_span, HashSet};
use anyhow::{anyhow, bail};
use formatter::Formatter;
use kanata_parser::cfg::{FileContentProvider, ParseError};
Expand All @@ -21,7 +18,6 @@ use serde_wasm_bindgen::{from_value, to_value};
use std::{
collections::BTreeMap,
fmt::Display,
iter::once,
path::{self, Path, PathBuf},
str::{FromStr, Split},
};
Expand Down Expand Up @@ -431,116 +427,19 @@ impl KanataLanguageServer {

let range = lsp_range_from_span(&root_span.into());

let start = helpers::now();

let defsrc_layout = match &self.workspace_options {
WorkspaceOptions::Single => {
let includes = match tree.includes() {
Ok(x) => x,
Err(e) => {
log!("{}", e);
return None;
}
};
if includes.is_empty() {
tree.defsrc_layout(params.options.tab_size)
} else {
None
}
}
WorkspaceOptions::Workspace {
main_config_file,
root,
} => {
let main_config_file_path = match PathBuf::from_str(main_config_file) {
Ok(x) => x,
Err(_) => {
log!("main_config_file is an invalid path");
return None;
}
};
let main_config_file_url = match path_to_url(&main_config_file_path, root) {
Ok(x) => x,
Err(_) => {
log!("failed to convert main_config_file_path to url");
return None;
}
};
let main_tree: ExtParseTree = if main_config_file_url == params.text_document.uri {
// currently opened file is the main file
tree.clone() // TODO: prevent clone
} else {
// currently opened file is non-main file, and probably an included file.
let text = &match self.documents.get(&params.text_document.uri) {
Some(doc) => &doc.text,
None => {
log!("included file is not present in the workspace");
return Some(vec![]);
}
};
match formatter::ext_tree::parse_into_ext_tree_and_root_span(text) {
Ok(x) => x.0,
Err(_) => {
log!("main file is not found in the workspace");
return None;
}
}
};

let includes = match main_tree.includes() {
Ok(x) => x,
Err(e) => {
log!("{}", e);
return None;
}
};
let includes = includes
.iter()
.map(|path| path_to_url(path, root))
.collect::<anyhow::Result<Vec<_>>>();
let includes = match includes {
Ok(x) => x,
Err(e) => {
log!("include path_to_url: {}", e);
return None;
}
};

// make sure that all includes collectively contain only 1 defsrc
let mut defsrc_layout = None;
for file_url in includes.iter().chain(once(&main_config_file_url)) {
let text = &self
.documents
.get(file_url)
.expect("document should be cached")
.text;

let (tree, _) =
match formatter::ext_tree::parse_into_ext_tree_and_root_span(text) {
Ok(x) => x,
Err(_e) => {
log!("failed to parse current file into tree");
return None;
}
};
if let Some(layout) = tree.defsrc_layout(params.options.tab_size) {
if defsrc_layout.is_none() {
defsrc_layout = Some(layout);
} else {
log!("multiple defsrc definitions across includes");
return None;
}
}
}
defsrc_layout
}
};
let defsrc_layout = formatter::defsrc_layout::get_defsrc_layout(
&self.workspace_options,
&self.documents,
params.options.tab_size,
&params.text_document.uri,
&tree,
)
.map_err(|e| log!("format: get_defsrc_layout: {}", e))
.ok()?;

self.formatter
.format(&mut tree, &params.options, defsrc_layout.as_deref());

log!("format in {:.3?}", helpers::now().duration_since(start));

Some(vec![TextEdit {
range,
new_text: tree.to_string(),
Expand Down

0 comments on commit 5fd0311

Please sign in to comment.