From 6a3cde3ef5c840ef573b5718edcdb1e761fbeee0 Mon Sep 17 00:00:00 2001 From: Samuel Kralik Date: Tue, 13 Oct 2026 10:05:55 +0200 Subject: [PATCH] Highlight binary expressions on operator hover Return the enclosing binary expression range with operator hover responses so editors highlight the complete operation instead of only showing hover content. --- pyrefly/lib/lsp/wasm/hover.rs | 5 +++-- pyrefly/lib/test/lsp/hover.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/pyrefly/lib/lsp/wasm/hover.rs b/pyrefly/lib/lsp/wasm/hover.rs index 23f0eedc05..e14830f5fd 100644 --- a/pyrefly/lib/lsp/wasm/hover.rs +++ b/pyrefly/lib/lsp/wasm/hover.rs @@ -1488,8 +1488,8 @@ pub fn get_hover_with_verbosity( return keyword_hover; }; - // `a and b and c` is a single flat BoolOp, so hovering any operator in the - // chain highlights the whole expression. `not` highlights its unary expression. + // Operators highlight their enclosing expression. `a and b and c` is a single flat + // BoolOp, so hovering any operator in the chain highlights the whole expression. let range = ast .as_deref() .zip(module_info.as_ref()) @@ -1501,6 +1501,7 @@ pub fn get_hover_with_verbosity( AnyNodeRef::ExprBoolOp(bool_op) => { Some(module_info.to_lsp_range(bool_op.range())) } + AnyNodeRef::ExprBinOp(bin_op) => Some(module_info.to_lsp_range(bin_op.range())), AnyNodeRef::ExprUnaryOp(unary_op) if unary_op.op == UnaryOp::Not => { Some(module_info.to_lsp_range(unary_op.range())) } diff --git a/pyrefly/lib/test/lsp/hover.rs b/pyrefly/lib/test/lsp/hover.rs index a0dde5bc59..cab29ad060 100644 --- a/pyrefly/lib/test/lsp/hover.rs +++ b/pyrefly/lib/test/lsp/hover.rs @@ -2606,6 +2606,32 @@ y = 1 or 2 ); } +#[test] +fn hover_over_binary_operator_highlights_binary_expression() { + let code = r#" +x = 1 | 2 +# ^ +"#; + let mut test_env = TestEnv::new(); + test_env.add("main", code); + let (state, handle) = test_env + .with_default_require_level(Require::Exports) + .to_state(); + let handle = handle("main"); + let position = extract_cursors_for_test(code)[0]; + let range = match get_hover(&state.transaction(), &handle, position, false) { + Some(hover) => hover.range, + None => panic!("Expected hover result for binary operator"), + }; + assert_eq!( + range, + Some(Range { + start: Position::new(1, 4), + end: Position::new(1, 9), + }) + ); +} + #[test] fn hover_over_not_operator_highlights_unary_expression() { let code = r#"