Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions crates/squawk_ide/src/code_actions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ mod rewrite_is_normalized_as_function_call;
mod rewrite_leading_from;
mod rewrite_normalize_as_function_call;
mod rewrite_not_equals_operator;
mod rewrite_null_predicate;
mod rewrite_overlaps_as_function_call;
mod rewrite_overlay_as_function_call;
mod rewrite_pattern_matching_as_operators;
Expand Down Expand Up @@ -72,6 +73,7 @@ use rewrite_is_normalized_as_function_call::rewrite_is_normalized_as_function_ca
use rewrite_leading_from::rewrite_leading_from;
use rewrite_normalize_as_function_call::rewrite_normalize_as_function_call;
use rewrite_not_equals_operator::rewrite_not_equals_operator;
use rewrite_null_predicate::rewrite_null_predicate;
use rewrite_overlaps_as_function_call::rewrite_overlaps_as_function_call;
use rewrite_overlay_as_function_call::rewrite_overlay_as_function_call;
use rewrite_pattern_matching_as_operators::rewrite_pattern_matching_as_operators;
Expand Down Expand Up @@ -127,6 +129,7 @@ pub fn code_actions(db: &dyn Db, position: InFile<TextSize>) -> Option<Vec<CodeA
rewrite_double_colon_to_cast(db, position, &mut actions);
rewrite_between_as_binary_expression(db, position, &mut actions);
rewrite_not_equals_operator(db, position, &mut actions);
rewrite_null_predicate(db, position, &mut actions);
rewrite_timestamp_type(db, position, &mut actions);
rewrite_unnest_as_rows_from(db, position, &mut actions);
rewrite_rows_from_as_unnest(db, position, &mut actions);
Expand Down
72 changes: 72 additions & 0 deletions crates/squawk_ide/src/code_actions/rewrite_null_predicate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use rowan::TextSize;
use salsa::Database as Db;
use squawk_linter::Edit;
use squawk_syntax::ast::{self, AstNode, PostfixOp};

use crate::{file::InFile, offsets::token_from_offset};

use super::{ActionKind, CodeAction};

pub(super) fn rewrite_null_predicate(
db: &dyn Db,
position: InFile<TextSize>,
actions: &mut Vec<CodeAction>,
) -> Option<()> {
let token = token_from_offset(db, position)?;
let postfix_expr = token.parent_ancestors().find_map(ast::PostfixExpr::cast)?;

let (op_token, replacement, title) = match postfix_expr.op()? {
PostfixOp::IsNull(token) => (token, "is null", "Rewrite as `IS NULL`"),
PostfixOp::NotNull(token) => (token, "is not null", "Rewrite as `IS NOT NULL`"),
_ => return None,
};

actions.push(CodeAction {
title: title.to_owned(),
edits: vec![Edit::replace(op_token.text_range(), replacement.to_owned())],
kind: ActionKind::RefactorRewrite,
});

Some(())
}

#[cfg(test)]
mod test {
use insta::assert_snapshot;

use crate::code_actions::test_utils::{apply_code_action, code_action_not_applicable};

use super::rewrite_null_predicate;

#[test]
fn rewrites_isnull() {
assert_snapshot!(
apply_code_action(rewrite_null_predicate, "select x is$0null from t;"),
@"select x is null from t;"
);
}

#[test]
fn rewrites_notnull() {
assert_snapshot!(
apply_code_action(rewrite_null_predicate, "select x not$0null from t;"),
@"select x is not null from t;"
);
}

#[test]
fn applies_when_cursor_is_on_the_value() {
assert_snapshot!(
apply_code_action(rewrite_null_predicate, "select val$0ue isnull from t;"),
@"select value is null from t;"
);
}

#[test]
fn is_not_applicable_outside_null_predicate() {
assert!(code_action_not_applicable(
rewrite_null_predicate,
"select val$0ue from t;"
));
}
}
Loading