+ let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
+ trim_multiline(snip, true, indent)
+}
+
+/// Returns a new Span that extends the original Span to the first non-whitespace char of the first
+/// line.
+///
+/// ```rust,ignore
+/// let x = ();
+/// // ^^
+/// // will be converted to
+/// let x = ();
+/// // ^^^^^^^^^^
+/// ```
+pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span {
+ if let Some(first_char_pos) = first_char_in_first_line(cx, span) {
+ span.with_lo(first_char_pos)
+ } else {
+ span
+ }
+}
+
+fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> {
+ let line_span = line_span(cx, span);
+ if let Some(snip) = snippet_opt(cx, line_span) {
+ snip.find(|c: char| !c.is_whitespace())
+ .map(|pos| line_span.lo() + BytePos::from_usize(pos))
+ } else {
+ None
+ }
+}
+
+/// Returns the indentation of the line of a span
+///
+/// ```rust,ignore
+/// let x = ();
+/// // ^^ -- will return 0
+/// let x = ();
+/// // ^^ -- will return 4
+/// ```
+pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
+ if let Some(snip) = snippet_opt(cx, line_span(cx, span)) {
+ snip.find(|c: char| !c.is_whitespace())
+ } else {
+ None
+ }