Skip to content

Commit

Permalink
Issue#7 Conversion for Language Level Type (#130)
Browse files Browse the repository at this point in the history
* adding drop possibility

* Add functionality drop type

* adding part of string as attribute

* adding string attr

* added grouped_parts

* added Quickfixes to add Type as attribute

* quickfix cardinality and attribute in one string

* fix Quickfix text

* quickfix endless while and error text

* added documentation

* drop constraint feature

* fixed drop function

* Adding default value for types

* delete test-uvl

* added docu for changed code_actions

* delete debugging info!()

* add attribute constraint synchro

* added documentation and try to fix attribute-constraint synchro

* checks the byte before and after the searched feature in the constraints

* fix convert type to attribute for constraints

* deleting test file

* update cargo.toml and delete test.uvl file

* fix changed regex version

* reset snapshot.clone to snapshot

---------

Co-authored-by: Martin Maurer <[email protected]>
Co-authored-by: Chico Sundermann <[email protected]>
  • Loading branch information
3 people authored Mar 21, 2024
1 parent 6b3952d commit f65b4d7
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 20 deletions.
4 changes: 2 additions & 2 deletions uvls/src/core/ast/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,7 @@ fn opt_constraint(state: &mut VisitorState) -> Option<ConstraintDecl> {
state.goto_field("lhs");
let lhs = opt_constraint(state)?;
state.goto_field("op");
info!("sind in op-constraint logic");

check_langlvls(state, LanguageLevel::Boolean(vec![]), true);
state.goto_field("rhs");
let rhs = opt_constraint(state)?;
Expand All @@ -933,7 +933,7 @@ fn opt_constraint(state: &mut VisitorState) -> Option<ConstraintDecl> {
state.goto_field("lhs");
let lhs = opt_numeric(state)?;
state.goto_field("op");
info!("sind in op-constraint equation");

check_langlvls(state, LanguageLevel::Arithmetic(vec![]), true);
state.goto_field("rhs");
let rhs = opt_numeric(state)?;
Expand Down
45 changes: 45 additions & 0 deletions uvls/src/core/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,51 @@ pub fn byte_to_line_col(byte: usize, source: &Rope) -> Position {
}
}

pub fn find_first_byte(target_str: &str, source: &Rope) -> Option<usize> {
let target_bytes = target_str.as_bytes();

for (index, _byte) in source.bytes().enumerate() {
if index + target_bytes.len() > source.len_bytes() {
break; // Avoid index out of bounds
}

let slice = &source.slice(index..index + target_bytes.len());
if slice.bytes().eq(target_bytes.iter().copied()) {
return Some(index);
}
}

None
}

pub fn find_all_first_bytes(target_str: &str, source: &Rope) -> Vec<usize> {
let target_bytes = target_str.as_bytes();
let mut indices = Vec::new();

let mut index = 0;
while index < source.len_bytes() {
if let Some(byte_index) = source
.slice(index..)
.bytes()
.position(|b| b == target_bytes[0])
{
let real_index = byte_index + index;
let slice = &source.slice(real_index..real_index + target_bytes.len());

if slice.bytes().eq(target_bytes.iter().copied()) {
indices.push(real_index);
index = real_index + 1;
} else {
index = real_index + 1;
}
} else {
break;
}
}

indices
}

pub fn containing_blk(mut node: Node) -> Option<Node> {
node = node.parent()?;
while node.kind() != "blk" {
Expand Down
114 changes: 96 additions & 18 deletions uvls/src/ide/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,43 +245,53 @@ pub fn starts_with_number(
}
}

/// Checks all possible types of quickfixes for the wrong/missing language_level
/// Checks all possible types of quickfixes for the wrong/missing language_level in features
pub fn wrong_language_level(
params: CodeActionParams,
diagnostic: Diagnostic,
snapshot: std::result::Result<Option<(Draft, Arc<RootGraph>)>, tower_lsp::jsonrpc::Error>,
) -> Result<Option<CodeActionResponse>> {
let mut result: Vec<CodeActionOrCommand> = vec![];
// adds the missing language level
match add_language_level(params.clone(), diagnostic.clone(), snapshot.clone()) {
Ok(Some(v)) => result.append(v.to_owned().as_mut()),
_ => (),
}
// the wrong feature type will be deleted
match drop_feature(params.clone(), diagnostic.clone(), snapshot.clone()) {
Ok(Some(v)) => result.append(v.to_owned().as_mut()),
_ => (),
}

// adds the feature type as an attribute
match add_type_as_attribute(params.clone(), diagnostic.clone(), snapshot.clone()) {
Ok(Some(v)) => result.append(v.to_owned().as_mut()),
_ => (),
}

// result set that is returned
return Ok(Some(result));
}


/// Checks all possible types of quickfixes for the wrong/missing language_level in constraints
pub fn wrong_language_level_constraint(
params: CodeActionParams,
diagnostic: Diagnostic,
snapshot: std::result::Result<Option<(Draft, Arc<RootGraph>)>, tower_lsp::jsonrpc::Error>,
) -> Result<Option<CodeActionResponse>> {
let mut result: Vec<CodeActionOrCommand> = vec![];
// adds the missing language level
match add_language_level(params.clone(), diagnostic.clone(), snapshot.clone()) {
Ok(Some(v)) => result.append(v.to_owned().as_mut()),
_ => (),
}
// the incorrect restriction is completely deleted
match drop_constraint(params.clone(), diagnostic.clone(), snapshot.clone()) {
Ok(Some(v)) => result.append(v.to_owned().as_mut()),
_ => (),
}

// result set that is returned
match convert_sum_constraint(params.clone(), diagnostic.clone(), snapshot.clone()) {
Ok(Some(v)) => result.append(v.to_owned().as_mut()),
_ => (),
Expand Down Expand Up @@ -371,6 +381,7 @@ pub fn add_language_level(
}
};

// assemble the corresponding code_action as a solution
let code_action_add_include = CodeAction {
title: format!("add {:?} to includes", new_include),
kind: Some(CodeActionKind::QUICKFIX),
Expand All @@ -390,6 +401,7 @@ pub fn add_language_level(
..Default::default()
};

// result set that is returned
let result = vec![CodeActionOrCommand::CodeAction(
code_action_add_include.clone(),
)];
Expand All @@ -399,6 +411,7 @@ pub fn add_language_level(
return Ok(None);
}
}

/// Adds the quickfix to completely delete the corresponding constraint
pub fn drop_constraint(
params: CodeActionParams,
Expand All @@ -409,6 +422,7 @@ pub fn drop_constraint(
let start_byte = byte_offset(&diagnostic.range.start, &source);
let mut end_byte = byte_offset(&diagnostic.range.end, &source);

// Find the end position at the end of the respective line and create the new range
while end_byte < source.len_chars()
&& source.slice(end_byte - 1..end_byte).as_str().unwrap() != "\r"
{
Expand All @@ -420,13 +434,16 @@ pub fn drop_constraint(
end: (byte_to_line_col(end_byte, &source)),
};

// Output name for quickfix / text of the complete line
let name = source
.slice(start_byte..end_byte)
.as_str()
.unwrap()
.replace("\n", "")
.replace("\r", "");
let code_action_drop = CodeAction {

// assemble the corresponding code_action as a solution
let code_action_drop_constraint = CodeAction {
title: format!("drop constraint: {:?}", name),
kind: Some(CodeActionKind::QUICKFIX),
edit: Some(WorkspaceEdit {
Expand All @@ -444,13 +461,16 @@ pub fn drop_constraint(
diagnostics: Some(vec![diagnostic.clone()]),
..Default::default()
};

// result set that is returned
return Ok(Some(vec![CodeActionOrCommand::CodeAction(
code_action_drop,
code_action_drop_constraint,
)]));
} else {
return Ok(None);
}
}

/// Adds the quickfix to roll out sum() functions
pub fn convert_sum_constraint(
params: CodeActionParams,
Expand Down Expand Up @@ -592,14 +612,17 @@ pub fn drop_feature(
let start_byte = byte_offset(&diagnostic.range.start, &source);
let end_byte = byte_offset(&diagnostic.range.end, &source);

// Name of feature including type
let name = source
.slice(start_byte..end_byte)
.as_str()
.unwrap()
.replace("\n", "")
.replace("\r", "");
let parts: Vec<&str> = name.split_whitespace().collect();
let code_action_drop = CodeAction {

// assemble the corresponding code_action as a solution
let code_action_drop_feature = CodeAction {
title: format!("drop {:?}", parts.first().unwrap().to_string()),
kind: Some(CodeActionKind::QUICKFIX),
edit: Some(WorkspaceEdit {
Expand All @@ -617,8 +640,10 @@ pub fn drop_feature(
diagnostics: Some(vec![diagnostic.clone()]),
..Default::default()
};

// result set that is returned
return Ok(Some(vec![CodeActionOrCommand::CodeAction(
code_action_drop,
code_action_drop_feature,
)]));
} else {
return Ok(None);
Expand All @@ -635,32 +660,34 @@ pub fn add_type_as_attribute(
let start_byte = byte_offset(&diagnostic.range.start, &source);
let mut end_byte = byte_offset(&diagnostic.range.end, &source);

// Find the end position at the end of the respective line and create the new range
while end_byte < source.len_chars()
&& source.slice(end_byte - 1..end_byte).as_str().unwrap() != "\r"
{
end_byte += 1;
}

let new_range: Range = Range {
let new_range_feature: Range = Range {
start: (diagnostic.range.start),
end: (byte_to_line_col(end_byte, &source)),
};

// text of the complete line
let mut name = source
.slice(start_byte..end_byte)
.as_str()
.unwrap()
.replace("\n", "")
.replace("\r", "");

//split cardinality string and attribute string
// split cardinality string and attribute string if available
if name.contains("]{") {
name = name.replace("]{", "] {");
}

// splitting the name into parts
let parts: Vec<&str> = name.split_whitespace().collect();

//create one part for cardinality
// create one part for cardinality
let mut has_cardinality = false;
let mut cardinality_string: String = String::new();

Expand Down Expand Up @@ -697,7 +724,7 @@ pub fn add_type_as_attribute(
}
}

//create grouped parts
// create grouped parts
// 0 = Current Type
// 1 = Feature name
// 2 = cardinality
Expand All @@ -712,6 +739,8 @@ pub fn add_type_as_attribute(
&spaced_cardinality_string
});
grouped_parts.push(&attributes_string);

// adds the default values ​​for the corresponding types
match grouped_parts.get(0).unwrap() {
&"Boolean" => grouped_parts.insert(4, " true"),
&"String" => grouped_parts.insert(4, " ''"),
Expand All @@ -720,8 +749,9 @@ pub fn add_type_as_attribute(
_ => info!("unknown type"),
};

//the result replaces the current line. here for no attributes
let mut result: String = format!(
// the result replaces the current line.
// here for no attributes
let mut result_feature_with_attribute: String = format!(
"{}{} {{{}{}}}",
grouped_parts.get(1).unwrap(),
grouped_parts.get(2).unwrap(),
Expand All @@ -741,7 +771,7 @@ pub fn add_type_as_attribute(
grouped_parts.get(4).unwrap()
);

result = format!(
result_feature_with_attribute = format!(
"{}{} {}",
grouped_parts.get(1).unwrap(),
grouped_parts.get(2).unwrap(),
Expand All @@ -750,6 +780,55 @@ pub fn add_type_as_attribute(
.to_string();
}

// Vector containing all strings to replace for the corresponding range
let mut text_edit_vector = vec![TextEdit {
range: new_range_feature,
new_text: result_feature_with_attribute,
}];

// all allowed characters before and after the feature, so as not to replace all occurrences of the string if they are only substrings of another string
let allowed_prefix_suffix = vec![' ', '>', '<', '=', '(', ')', '\r'];

// method checks if constraints is available in file
if let Some(index_constraints) = find_first_byte("constraints", &source) {
// returns a vector with all first bytes of the string
let indices = find_all_first_bytes(grouped_parts.get(1).unwrap(), &source);
if !indices.is_empty() {
// for loop irritates all indices
for index in indices {
// Only if the feature occurs in the constraints will it be edited
if index > index_constraints && index < source.len_bytes() {
let char_before_feature = char::from(source.byte(index - 1));
let mut char_after_feature: char = ' ';
if index + grouped_parts.get(1).unwrap().len() < source.len_bytes() {
char_after_feature = char::from(
source.byte(index + grouped_parts.get(1).unwrap().len()),
);
}
// If the character before and after matches the permitted characters, the string is replaced in the attributes
if allowed_prefix_suffix.contains(&char_before_feature)
&& allowed_prefix_suffix.contains(&char_after_feature)
{
let constraint_range: Range = Range {
start: byte_to_line_col(index, &source),
end: byte_to_line_col(
index + grouped_parts.get(1).unwrap().len(),
&source,
),
};
let feature_with_attribute_constraint =
format!("{}.{}", grouped_parts[1], grouped_parts[0]);
text_edit_vector.push(TextEdit {
range: constraint_range,
new_text: feature_with_attribute_constraint,
});
}
}
}
}
}

// assemble the corresponding code_action as a solution
let code_action_add_type_as_attribute = CodeAction {
title: format!(
"convert {:?} to attribute",
Expand All @@ -759,10 +838,7 @@ pub fn add_type_as_attribute(
edit: Some(WorkspaceEdit {
changes: Some(HashMap::<Url, Vec<TextEdit>>::from([(
params.text_document.uri.clone(),
vec![TextEdit {
range: new_range,
new_text: result,
}],
text_edit_vector,
)])),
document_changes: None,
change_annotations: None,
Expand All @@ -771,6 +847,8 @@ pub fn add_type_as_attribute(
diagnostics: Some(vec![diagnostic.clone()]),
..Default::default()
};

// result set that is returned
return Ok(Some(vec![CodeActionOrCommand::CodeAction(
code_action_add_type_as_attribute,
)]));
Expand Down

0 comments on commit f65b4d7

Please sign in to comment.