pub mod add_mut;
pub mod ast_utils;
pub mod batch;
pub mod change_signature;
pub mod extract_function;
pub mod inline;
pub mod introduce_variable;
pub mod move_item;
pub mod preview;
pub mod scope_analyzer;
use crate::database::WindjammerDatabase;
use tower_lsp::lsp_types::*;
pub struct RefactoringEngine<'a> {
db: &'a WindjammerDatabase,
}
impl<'a> RefactoringEngine<'a> {
pub fn new(db: &'a WindjammerDatabase) -> Self {
Self { db }
}
#[allow(dead_code)]
pub fn db(&self) -> &WindjammerDatabase {
self.db
}
pub fn code_actions(
&self,
uri: &Url,
range: Range,
context: &CodeActionContext,
source: Option<&str>,
) -> Vec<CodeActionOrCommand> {
let mut actions = Vec::new();
if let Some(source) = source {
for diagnostic in &context.diagnostics {
if let Some(action) = self.add_mut_action(uri, diagnostic, source) {
actions.push(CodeActionOrCommand::CodeAction(action));
}
}
}
if range.start != range.end {
if let Some(action) = self.extract_function_action(uri, range) {
actions.push(CodeActionOrCommand::CodeAction(action));
}
}
if let Some(action) = self.inline_variable_action(uri, range.start) {
actions.push(CodeActionOrCommand::CodeAction(action));
}
if range.start != range.end {
if let Some(action) = self.introduce_variable_action(uri, range) {
actions.push(CodeActionOrCommand::CodeAction(action));
}
}
actions
}
fn add_mut_action(
&self,
uri: &Url,
diagnostic: &Diagnostic,
source: &str,
) -> Option<CodeAction> {
let message = &diagnostic.message;
if !message.contains("immutable") && !message.contains("cannot assign") {
return None;
}
let var_name = if let Some(start) = message.find('`') {
if let Some(end) = message[start + 1..].find('`') {
message[start + 1..start + 1 + end].to_string()
} else {
return None;
}
} else {
return None;
};
let fix =
add_mut::AddMutFix::new(uri.clone(), var_name.clone(), diagnostic.range.start.line);
let edit = fix.execute(source).ok()?;
Some(CodeAction {
title: format!("Add `mut` to `{}`", var_name),
kind: Some(CodeActionKind::QUICKFIX),
diagnostics: Some(vec![diagnostic.clone()]),
edit: Some(edit),
command: None,
is_preferred: Some(true),
disabled: None,
data: None,
})
}
fn extract_function_action(&self, uri: &Url, range: Range) -> Option<CodeAction> {
Some(CodeAction {
title: "Extract Function".to_string(),
kind: Some(CodeActionKind::REFACTOR_EXTRACT),
diagnostics: None,
edit: None, command: Some(Command {
title: "Extract Function".to_string(),
command: "windjammer.refactor.extractFunction".to_string(),
arguments: Some(vec![
serde_json::to_value(uri).ok()?,
serde_json::to_value(range).ok()?,
]),
}),
is_preferred: Some(true),
disabled: None,
data: None,
})
}
fn inline_variable_action(&self, _uri: &Url, _position: Position) -> Option<CodeAction> {
None
}
fn introduce_variable_action(&self, _uri: &Url, _range: Range) -> Option<CodeAction> {
None
}
#[allow(dead_code)]
pub fn execute_extract_function(
&self,
uri: &Url,
range: Range,
function_name: &str,
source: &str,
) -> Result<WorkspaceEdit, String> {
let extractor = extract_function::ExtractFunction::new(self.db, uri.clone(), range);
extractor.execute(function_name, source)
}
#[allow(dead_code)]
pub fn execute_inline_variable(
&self,
uri: &Url,
position: Position,
source: &str,
) -> Result<WorkspaceEdit, String> {
let inliner = inline::InlineRefactoring::new(self.db, uri.clone(), position);
inliner.execute(source)
}
#[allow(dead_code)]
pub fn execute_introduce_variable(
&self,
uri: &Url,
range: Range,
variable_name: &str,
source: &str,
) -> Result<WorkspaceEdit, String> {
let introducer = introduce_variable::IntroduceVariable::new(self.db, uri.clone(), range);
introducer.execute(variable_name, source)
}
#[allow(dead_code)]
pub fn execute_change_signature(
&self,
uri: &Url,
position: Position,
changes: &[change_signature::ParameterChange],
source: &str,
) -> Result<WorkspaceEdit, String> {
let changer = change_signature::ChangeSignature::new(self.db, uri.clone(), position);
changer.execute(changes, source)
}
#[allow(dead_code)]
pub fn execute_move_item(
&self,
source_uri: &Url,
target_uri: &Url,
position: Position,
source_content: &str,
target_content: &str,
) -> Result<WorkspaceEdit, String> {
let mover =
move_item::MoveItem::new(self.db, source_uri.clone(), target_uri.clone(), position);
mover.execute(source_content, target_content)
}
#[allow(dead_code)]
pub fn execute_add_mut(
&self,
uri: &Url,
variable_name: &str,
line: u32,
source: &str,
) -> Result<WorkspaceEdit, String> {
let fix = add_mut::AddMutFix::new(uri.clone(), variable_name.to_string(), line);
fix.execute(source)
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct RefactoringResult {
pub edit: WorkspaceEdit,
pub preview: String,
pub message: String,
}
#[cfg(test)]
mod tests {
#[test]
fn test_refactoring_engine_creation() {
}
}