mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 10:48:32 +00:00
test: another update to add back a caret
This change also requires some shuffling to the offsets we generate for the diagnostic. Previously, we were generating an empty range immediately *after* the line terminator and immediate before the first byte of the subsequent line. How this is rendered is somewhat open to interpretation, but the new version of `annotate-snippets` chooses to render this at the end of the preceding line instead of the beginning of the following line. In this case, we want the diagnostic to point to the beginning of the following line. So we either need to change `annotate-snippets` to render such spans at the beginning of the following line, or we need to change our span to point to the first full character in the following line. The latter will force `annotate-snippets` to move the caret to the proper location. I ended up deciding to change our spans instead of changing how `annotate-snippets` renders empty spans after a line terminator. While I didn't investigate it, my guess is that they probably had good reason for doing so, and it doesn't necessarily strike me as _wrong_. Furthermore, fixing up our spans seems like a good idea regardless, and was pretty easy to do.
This commit is contained in:
parent
75b4ed5ad1
commit
5021f32449
4 changed files with 96 additions and 12 deletions
|
@ -3,7 +3,7 @@ use ruff_python_codegen::Stylist;
|
|||
use ruff_python_index::Indexer;
|
||||
use ruff_python_parser::{TokenKind, Tokens};
|
||||
use ruff_source_file::LineRanges;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
|
||||
use crate::line_width::IndentWidth;
|
||||
use crate::registry::{AsRule, Rule};
|
||||
|
@ -161,7 +161,13 @@ pub(crate) fn check_logical_lines(
|
|||
let range = if first_token.kind() == TokenKind::Indent {
|
||||
first_token.range()
|
||||
} else {
|
||||
TextRange::new(locator.line_start(first_token.start()), first_token.start())
|
||||
let mut range =
|
||||
TextRange::new(locator.line_start(first_token.start()), first_token.start());
|
||||
if range.is_empty() {
|
||||
let end = locator.ceil_char_boundary(range.start() + TextSize::from(1));
|
||||
range = TextRange::new(range.start(), end);
|
||||
}
|
||||
range
|
||||
};
|
||||
|
||||
let indent_level = expand_indent(locator.slice(range), settings.tab_size);
|
||||
|
|
|
@ -118,6 +118,86 @@ impl<'a> Locator<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Finds the closest [`TextSize`] not less than the offset given for which
|
||||
/// `is_char_boundary` is `true`. Unless the offset given is greater than
|
||||
/// the length of the underlying contents, in which case, the length of the
|
||||
/// contents is returned.
|
||||
///
|
||||
/// Can be replaced with `str::ceil_char_boundary` once it's stable.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// From `std`:
|
||||
///
|
||||
/// ```
|
||||
/// use ruff_text_size::{Ranged, TextSize};
|
||||
/// use ruff_linter::Locator;
|
||||
///
|
||||
/// let locator = Locator::new("❤️🧡💛💚💙💜");
|
||||
/// assert_eq!(locator.text_len(), TextSize::from(26));
|
||||
/// assert!(!locator.contents().is_char_boundary(13));
|
||||
///
|
||||
/// let closest = locator.ceil_char_boundary(TextSize::from(13));
|
||||
/// assert_eq!(closest, TextSize::from(14));
|
||||
/// assert_eq!(&locator.contents()[..closest.to_usize()], "❤️🧡💛");
|
||||
/// ```
|
||||
///
|
||||
/// Additional examples:
|
||||
///
|
||||
/// ```
|
||||
/// use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||
/// use ruff_linter::Locator;
|
||||
///
|
||||
/// let locator = Locator::new("Hello");
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// locator.ceil_char_boundary(TextSize::from(0)),
|
||||
/// TextSize::from(0)
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// locator.ceil_char_boundary(TextSize::from(5)),
|
||||
/// TextSize::from(5)
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// locator.ceil_char_boundary(TextSize::from(6)),
|
||||
/// TextSize::from(5)
|
||||
/// );
|
||||
///
|
||||
/// let locator = Locator::new("α");
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// locator.ceil_char_boundary(TextSize::from(0)),
|
||||
/// TextSize::from(0)
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// locator.ceil_char_boundary(TextSize::from(1)),
|
||||
/// TextSize::from(2)
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// locator.ceil_char_boundary(TextSize::from(2)),
|
||||
/// TextSize::from(2)
|
||||
/// );
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// locator.ceil_char_boundary(TextSize::from(3)),
|
||||
/// TextSize::from(2)
|
||||
/// );
|
||||
/// ```
|
||||
pub fn ceil_char_boundary(&self, offset: TextSize) -> TextSize {
|
||||
let upper_bound = offset
|
||||
.to_u32()
|
||||
.saturating_add(4)
|
||||
.min(self.text_len().to_u32());
|
||||
(offset.to_u32()..upper_bound)
|
||||
.map(TextSize::from)
|
||||
.find(|offset| self.contents.is_char_boundary(offset.to_usize()))
|
||||
.unwrap_or_else(|| TextSize::from(upper_bound))
|
||||
}
|
||||
|
||||
/// Take the source code between the given [`TextRange`].
|
||||
#[inline]
|
||||
pub fn slice<T: Ranged>(&self, ranged: T) -> &'a str {
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
E11.py:9:1: E112 Expected an indented block
|
||||
|
|
||||
7 | #: E112
|
||||
8 | if False:
|
||||
9 | print()
|
||||
| E112
|
||||
| ^ E112
|
||||
10 | #: E113
|
||||
11 | print()
|
||||
|
|
||||
|
@ -47,7 +46,7 @@ E11.py:45:1: E112 Expected an indented block
|
|||
43 | #: E112
|
||||
44 | if False: #
|
||||
45 | print()
|
||||
| E112
|
||||
| ^ E112
|
||||
46 | #:
|
||||
47 | if False:
|
||||
|
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/pycodestyle/mod.rs
|
||||
snapshot_kind: text
|
||||
---
|
||||
E11.py:9:1: SyntaxError: Expected an indented block after `if` statement
|
||||
|
|
||||
|
@ -37,7 +36,7 @@ E11.py:30:1: E115 Expected an indented block (comment)
|
|||
28 | def start(self):
|
||||
29 | if True:
|
||||
30 | # try:
|
||||
| E115
|
||||
| ^ E115
|
||||
31 | # self.master.start()
|
||||
32 | # except MasterExit:
|
||||
|
|
||||
|
@ -47,7 +46,7 @@ E11.py:31:1: E115 Expected an indented block (comment)
|
|||
29 | if True:
|
||||
30 | # try:
|
||||
31 | # self.master.start()
|
||||
| E115
|
||||
| ^ E115
|
||||
32 | # except MasterExit:
|
||||
33 | # self.shutdown()
|
||||
|
|
||||
|
@ -57,7 +56,7 @@ E11.py:32:1: E115 Expected an indented block (comment)
|
|||
30 | # try:
|
||||
31 | # self.master.start()
|
||||
32 | # except MasterExit:
|
||||
| E115
|
||||
| ^ E115
|
||||
33 | # self.shutdown()
|
||||
34 | # finally:
|
||||
|
|
||||
|
@ -67,7 +66,7 @@ E11.py:33:1: E115 Expected an indented block (comment)
|
|||
31 | # self.master.start()
|
||||
32 | # except MasterExit:
|
||||
33 | # self.shutdown()
|
||||
| E115
|
||||
| ^ E115
|
||||
34 | # finally:
|
||||
35 | # sys.exit()
|
||||
|
|
||||
|
@ -77,7 +76,7 @@ E11.py:34:1: E115 Expected an indented block (comment)
|
|||
32 | # except MasterExit:
|
||||
33 | # self.shutdown()
|
||||
34 | # finally:
|
||||
| E115
|
||||
| ^ E115
|
||||
35 | # sys.exit()
|
||||
36 | self.master.start()
|
||||
|
|
||||
|
@ -87,7 +86,7 @@ E11.py:35:1: E115 Expected an indented block (comment)
|
|||
33 | # self.shutdown()
|
||||
34 | # finally:
|
||||
35 | # sys.exit()
|
||||
| E115
|
||||
| ^ E115
|
||||
36 | self.master.start()
|
||||
37 | #: E117
|
||||
|
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue