mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-22 04:34:51 +00:00
Merge pull request #1007 from kamalmarhubi/basic-line-ranges-v2
Add infrastructure for formatting specific line ranges
This commit is contained in:
commit
240dba5467
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -7,6 +7,7 @@ dependencies = [
|
||||
"getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"multimap 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.1.71 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strings 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -80,6 +81,11 @@ dependencies = [
|
||||
"libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multimap"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "0.1.71"
|
||||
|
@ -26,3 +26,4 @@ log = "0.3"
|
||||
env_logger = "0.3"
|
||||
getopts = "0.2"
|
||||
itertools = "0.4.15"
|
||||
multimap = "0.3"
|
||||
|
19
README.md
19
README.md
@ -73,6 +73,25 @@ the command line. For example `rustfmt --write-mode=display src/filename.rs`
|
||||
|
||||
`cargo fmt` uses `--write-mode=replace` by default.
|
||||
|
||||
If you want to restrict reformatting to specific sets of lines, you can
|
||||
use the `--file-lines` option. Its argument is a JSON array of objects
|
||||
with `file` and `range` properties, where `file` is a file name, and
|
||||
`range` is an array representing a range of lines like `[7,13]`. Ranges
|
||||
are 1-based and inclusive of both end points. Specifying an empty array
|
||||
will result in no files being formatted. For example,
|
||||
|
||||
```
|
||||
rustfmt --file-lines '[
|
||||
{"file":"src/lib.rs","range":[7,13]},
|
||||
{"file":"src/lib.rs","range":[21,29]},
|
||||
{"file":"src/foo.rs","range":[10,11]},
|
||||
{"file":"src/foo.rs","range":[15,15]}]'
|
||||
```
|
||||
|
||||
would format lines `7-13` and `21-29` of `src/lib.rs`, and lines `10-11`,
|
||||
and `15` of `src/foo.rs`. No other files would be formatted, even if they
|
||||
are included as out of line modules from `src/lib.rs`.
|
||||
|
||||
If `rustfmt` successfully reformatted the code it will exit with `0` exit
|
||||
status. Exit status `1` signals some unexpected error, like an unknown option or
|
||||
a failure to read a file. Exit status `2` is returned if there are syntax errors
|
||||
|
@ -18,6 +18,7 @@ extern crate env_logger;
|
||||
extern crate getopts;
|
||||
|
||||
use rustfmt::{run, Input, Summary};
|
||||
use rustfmt::file_lines::FileLines;
|
||||
use rustfmt::config::{Config, WriteMode};
|
||||
|
||||
use std::{env, error};
|
||||
@ -57,6 +58,7 @@ struct CliOptions {
|
||||
skip_children: bool,
|
||||
verbose: bool,
|
||||
write_mode: Option<WriteMode>,
|
||||
file_lines: FileLines, // Default is all lines in all files.
|
||||
}
|
||||
|
||||
impl CliOptions {
|
||||
@ -73,12 +75,17 @@ impl CliOptions {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref file_lines) = matches.opt_str("file-lines") {
|
||||
options.file_lines = try!(file_lines.parse());
|
||||
}
|
||||
|
||||
Ok(options)
|
||||
}
|
||||
|
||||
fn apply_to(&self, config: &mut Config) {
|
||||
fn apply_to(self, config: &mut Config) {
|
||||
config.skip_children = self.skip_children;
|
||||
config.verbose = self.verbose;
|
||||
config.file_lines = self.file_lines;
|
||||
if let Some(write_mode) = self.write_mode {
|
||||
config.write_mode = write_mode;
|
||||
}
|
||||
@ -168,6 +175,10 @@ fn make_opts() -> Options {
|
||||
"Recursively searches the given path for the rustfmt.toml config file. If not \
|
||||
found reverts to the input file path",
|
||||
"[Path for the configuration file]");
|
||||
opts.optopt("",
|
||||
"file-lines",
|
||||
"Format specified line ranges. See README for more detail on the JSON format.",
|
||||
"JSON");
|
||||
|
||||
opts
|
||||
}
|
||||
@ -198,8 +209,12 @@ fn execute(opts: &Options) -> FmtResult<Summary> {
|
||||
|
||||
Ok(run(Input::Text(input), &config))
|
||||
}
|
||||
Operation::Format { files, config_path } => {
|
||||
Operation::Format { mut files, config_path } => {
|
||||
let options = try!(CliOptions::from_matches(&matches));
|
||||
|
||||
// Add any additional files that were specified via `--file-lines`.
|
||||
files.extend(options.file_lines.files().cloned().map(PathBuf::from));
|
||||
|
||||
let mut config = Config::default();
|
||||
let mut path = None;
|
||||
// Load the config path file if provided
|
||||
@ -227,7 +242,7 @@ fn execute(opts: &Options) -> FmtResult<Summary> {
|
||||
config = config_tmp;
|
||||
}
|
||||
|
||||
options.apply_to(&mut config);
|
||||
options.clone().apply_to(&mut config);
|
||||
error_summary.add(run(Input::File(file), &config));
|
||||
}
|
||||
Ok(error_summary)
|
||||
@ -306,8 +321,8 @@ fn determine_operation(matches: &Matches) -> FmtResult<Operation> {
|
||||
Some(dir)
|
||||
});
|
||||
|
||||
// if no file argument is supplied, read from stdin
|
||||
if matches.free.is_empty() {
|
||||
// if no file argument is supplied and `--file-lines` is not specified, read from stdin
|
||||
if matches.free.is_empty() && !matches.opt_present("file-lines") {
|
||||
|
||||
let mut buffer = String::new();
|
||||
try!(io::stdin().read_to_string(&mut buffer));
|
||||
@ -318,6 +333,7 @@ fn determine_operation(matches: &Matches) -> FmtResult<Operation> {
|
||||
});
|
||||
}
|
||||
|
||||
// We append files from `--file-lines` later in `execute()`.
|
||||
let files: Vec<_> = matches.free.iter().map(PathBuf::from).collect();
|
||||
|
||||
Ok(Operation::Format {
|
||||
|
94
src/codemap.rs
Normal file
94
src/codemap.rs
Normal file
@ -0,0 +1,94 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! This module contains utilities that work with the `CodeMap` from libsyntax / syntex_syntax.
|
||||
//! This includes extension traits and methods for looking up spans and line ranges for AST nodes.
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
use syntax::codemap::{BytePos, CodeMap, FileMap, Span};
|
||||
|
||||
use comment::FindUncommented;
|
||||
|
||||
/// A range of lines in a file, inclusive of both ends.
|
||||
pub struct LineRange {
|
||||
pub file: Rc<FileMap>,
|
||||
pub lo: usize,
|
||||
pub hi: usize,
|
||||
}
|
||||
|
||||
impl LineRange {
|
||||
pub fn file_name(&self) -> &str {
|
||||
self.file.as_ref().name.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SpanUtils {
|
||||
fn span_after(&self, original: Span, needle: &str) -> BytePos;
|
||||
fn span_after_last(&self, original: Span, needle: &str) -> BytePos;
|
||||
fn span_before(&self, original: Span, needle: &str) -> BytePos;
|
||||
}
|
||||
|
||||
pub trait LineRangeUtils {
|
||||
/// Returns the `LineRange` that corresponds to `span` in `self`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `span` crosses a file boundary, which shouldn't happen.
|
||||
fn lookup_line_range(&self, span: Span) -> LineRange;
|
||||
}
|
||||
|
||||
impl SpanUtils for CodeMap {
|
||||
#[inline]
|
||||
fn span_after(&self, original: Span, needle: &str) -> BytePos {
|
||||
let snippet = self.span_to_snippet(original).unwrap();
|
||||
let offset = snippet.find_uncommented(needle).unwrap() + needle.len();
|
||||
|
||||
original.lo + BytePos(offset as u32)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn span_after_last(&self, original: Span, needle: &str) -> BytePos {
|
||||
let snippet = self.span_to_snippet(original).unwrap();
|
||||
let mut offset = 0;
|
||||
|
||||
while let Some(additional_offset) = snippet[offset..].find_uncommented(needle) {
|
||||
offset += additional_offset + needle.len();
|
||||
}
|
||||
|
||||
original.lo + BytePos(offset as u32)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn span_before(&self, original: Span, needle: &str) -> BytePos {
|
||||
let snippet = self.span_to_snippet(original).unwrap();
|
||||
let offset = snippet.find_uncommented(needle).unwrap();
|
||||
|
||||
original.lo + BytePos(offset as u32)
|
||||
}
|
||||
}
|
||||
|
||||
impl LineRangeUtils for CodeMap {
|
||||
fn lookup_line_range(&self, span: Span) -> LineRange {
|
||||
let lo = self.lookup_char_pos(span.lo);
|
||||
let hi = self.lookup_char_pos(span.hi);
|
||||
|
||||
assert!(lo.file.name == hi.file.name,
|
||||
"span crossed file boundary: lo: {:?}, hi: {:?}",
|
||||
lo,
|
||||
hi);
|
||||
|
||||
LineRange {
|
||||
file: lo.file.clone(),
|
||||
lo: lo.line,
|
||||
hi: hi.line,
|
||||
}
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@
|
||||
|
||||
extern crate toml;
|
||||
|
||||
use file_lines::FileLines;
|
||||
use lists::{SeparatorTactic, ListTactic};
|
||||
use std::io::Write;
|
||||
|
||||
@ -200,6 +201,12 @@ impl ConfigType for String {
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigType for FileLines {
|
||||
fn doc_hint() -> String {
|
||||
String::from("<json>")
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ConfigHelpItem {
|
||||
option_name: &'static str,
|
||||
doc_string: &'static str,
|
||||
@ -327,6 +334,9 @@ macro_rules! create_config {
|
||||
create_config! {
|
||||
verbose: bool, false, "Use verbose output";
|
||||
skip_children: bool, false, "Don't reformat out of line modules";
|
||||
file_lines: FileLines, FileLines::all(),
|
||||
"Lines to format; this is not supported in rustfmt.toml, and can only be specified \
|
||||
via the --file-lines option";
|
||||
max_width: usize, 100, "Maximum width of each line";
|
||||
ideal_width: usize, 80, "Ideal width of each line";
|
||||
tab_spaces: usize, 4, "Number of spaces per tab";
|
||||
|
@ -16,12 +16,13 @@ use std::iter::ExactSizeIterator;
|
||||
use std::fmt::Write;
|
||||
|
||||
use {Indent, Spanned};
|
||||
use codemap::SpanUtils;
|
||||
use rewrite::{Rewrite, RewriteContext};
|
||||
use lists::{write_list, itemize_list, ListFormatting, SeparatorTactic, ListTactic,
|
||||
DefinitiveListTactic, definitive_tactic, ListItem, format_item_list};
|
||||
use string::{StringFormat, rewrite_string};
|
||||
use utils::{CodeMapSpanUtils, extra_offset, last_line_width, wrap_str, binary_search,
|
||||
first_line_width, semicolon_for_stmt, trimmed_last_line_width, left_most_sub_expr};
|
||||
use utils::{extra_offset, last_line_width, wrap_str, binary_search, first_line_width,
|
||||
semicolon_for_stmt, trimmed_last_line_width, left_most_sub_expr};
|
||||
use visitor::FmtVisitor;
|
||||
use config::{Config, StructLitStyle, MultilineStyle, ElseIfBraceStyle, ControlBraceStyle};
|
||||
use comment::{FindUncommented, rewrite_comment, contains_comment, recover_comment_removed};
|
||||
|
232
src/file_lines.rs
Normal file
232
src/file_lines.rs
Normal file
@ -0,0 +1,232 @@
|
||||
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! This module contains types and functions to support formatting specific line ranges.
|
||||
use std::{cmp, iter, str};
|
||||
|
||||
use itertools::Itertools;
|
||||
use multimap::MultiMap;
|
||||
use rustc_serialize::{self, json};
|
||||
|
||||
use codemap::LineRange;
|
||||
|
||||
/// A range that is inclusive of both ends.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, RustcDecodable)]
|
||||
struct Range {
|
||||
pub lo: usize,
|
||||
pub hi: usize,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a LineRange> for Range {
|
||||
fn from(range: &'a LineRange) -> Range {
|
||||
Range::new(range.lo, range.hi)
|
||||
}
|
||||
}
|
||||
|
||||
impl Range {
|
||||
fn new(lo: usize, hi: usize) -> Range {
|
||||
Range { lo: lo, hi: hi }
|
||||
}
|
||||
|
||||
fn is_empty(self) -> bool {
|
||||
self.lo > self.hi
|
||||
}
|
||||
|
||||
fn contains(self, other: Range) -> bool {
|
||||
if other.is_empty() {
|
||||
true
|
||||
} else {
|
||||
!self.is_empty() && self.lo <= other.lo && self.hi >= other.hi
|
||||
}
|
||||
}
|
||||
|
||||
fn intersects(self, other: Range) -> bool {
|
||||
if self.is_empty() || other.is_empty() {
|
||||
false
|
||||
} else {
|
||||
(self.lo <= other.hi && other.hi <= self.hi) ||
|
||||
(other.lo <= self.hi && self.hi <= other.hi)
|
||||
}
|
||||
}
|
||||
|
||||
fn adjacent_to(self, other: Range) -> bool {
|
||||
if self.is_empty() || other.is_empty() {
|
||||
false
|
||||
} else {
|
||||
self.hi + 1 == other.lo || other.hi + 1 == self.lo
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new `Range` with lines from `self` and `other` if they were adjacent or
|
||||
/// intersect; returns `None` otherwise.
|
||||
fn merge(self, other: Range) -> Option<Range> {
|
||||
if self.adjacent_to(other) || self.intersects(other) {
|
||||
Some(Range::new(cmp::min(self.lo, other.lo), cmp::max(self.hi, other.hi)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A set of lines in files.
|
||||
///
|
||||
/// It is represented as a multimap keyed on file names, with values a collection of
|
||||
/// non-overlapping ranges sorted by their start point. An inner `None` is interpreted to mean all
|
||||
/// lines in all files.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct FileLines(Option<MultiMap<String, Range>>);
|
||||
|
||||
/// Normalizes the ranges so that the invariants for `FileLines` hold: ranges are non-overlapping,
|
||||
/// and ordered by their start point.
|
||||
fn normalize_ranges(map: &mut MultiMap<String, Range>) {
|
||||
for (_, ranges) in map.iter_all_mut() {
|
||||
ranges.sort_by_key(|x| x.lo);
|
||||
let merged = ranges.drain(..).coalesce(|x, y| x.merge(y).ok_or((x, y))).collect();
|
||||
*ranges = merged;
|
||||
}
|
||||
}
|
||||
|
||||
impl FileLines {
|
||||
/// Creates a `FileLines` that contains all lines in all files.
|
||||
pub fn all() -> FileLines {
|
||||
FileLines(None)
|
||||
}
|
||||
|
||||
/// Creates a `FileLines` from a `MultiMap`, ensuring that the invariants hold.
|
||||
fn from_multimap(map: MultiMap<String, Range>) -> FileLines {
|
||||
let mut map = map;
|
||||
normalize_ranges(&mut map);
|
||||
FileLines(Some(map))
|
||||
}
|
||||
|
||||
/// Returns an iterator over the files contained in `self`.
|
||||
pub fn files(&self) -> Files {
|
||||
Files(self.0.as_ref().map(MultiMap::keys))
|
||||
}
|
||||
|
||||
/// Returns true if `range` is fully contained in `self`.
|
||||
pub fn contains(&self, range: &LineRange) -> bool {
|
||||
let map = match self.0 {
|
||||
// `None` means "all lines in all files".
|
||||
None => return true,
|
||||
Some(ref map) => map,
|
||||
};
|
||||
|
||||
match map.get_vec(range.file_name()) {
|
||||
None => false,
|
||||
Some(ranges) => ranges.iter().any(|r| r.contains(Range::from(range))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if any lines in `range` are in `self`.
|
||||
pub fn intersects(&self, range: &LineRange) -> bool {
|
||||
let map = match self.0 {
|
||||
// `None` means "all lines in all files".
|
||||
None => return true,
|
||||
Some(ref map) => map,
|
||||
};
|
||||
|
||||
match map.get_vec(range.file_name()) {
|
||||
None => false,
|
||||
Some(ranges) => ranges.iter().any(|r| r.intersects(Range::from(range))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// FileLines files iterator.
|
||||
pub struct Files<'a>(Option<::std::collections::hash_map::Keys<'a, String, Vec<Range>>>);
|
||||
|
||||
impl<'a> iter::Iterator for Files<'a> {
|
||||
type Item = &'a String;
|
||||
|
||||
fn next(&mut self) -> Option<&'a String> {
|
||||
self.0.as_mut().and_then(Iterator::next)
|
||||
}
|
||||
}
|
||||
|
||||
// This impl is needed for `Config::override_value` to work for use in tests.
|
||||
impl str::FromStr for FileLines {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<FileLines, String> {
|
||||
let v: Vec<JsonSpan> = try!(json::decode(s).map_err(|e| e.to_string()));
|
||||
let m = v.into_iter().map(JsonSpan::into_tuple).collect();
|
||||
Ok(FileLines::from_multimap(m))
|
||||
}
|
||||
}
|
||||
|
||||
// For JSON decoding.
|
||||
#[derive(Clone, Debug, RustcDecodable)]
|
||||
struct JsonSpan {
|
||||
file: String,
|
||||
range: (usize, usize),
|
||||
}
|
||||
|
||||
impl JsonSpan {
|
||||
// To allow `collect()`ing into a `MultiMap`.
|
||||
fn into_tuple(self) -> (String, Range) {
|
||||
let (lo, hi) = self.range;
|
||||
(self.file, Range::new(lo, hi))
|
||||
}
|
||||
}
|
||||
|
||||
// This impl is needed for inclusion in the `Config` struct. We don't have a toml representation
|
||||
// for `FileLines`, so it will just panic instead.
|
||||
impl rustc_serialize::Decodable for FileLines {
|
||||
fn decode<D: rustc_serialize::Decoder>(_: &mut D) -> Result<Self, D::Error> {
|
||||
panic!("FileLines cannot be deserialized from a project rustfmt.toml file: please \
|
||||
specify it via the `--file-lines` option instead");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::Range;
|
||||
|
||||
#[test]
|
||||
fn test_range_intersects() {
|
||||
assert!(Range::new(1, 2).intersects(Range::new(1, 1)));
|
||||
assert!(Range::new(1, 2).intersects(Range::new(2, 2)));
|
||||
assert!(!Range::new(1, 2).intersects(Range::new(0, 0)));
|
||||
assert!(!Range::new(1, 2).intersects(Range::new(3, 10)));
|
||||
assert!(!Range::new(1, 3).intersects(Range::new(5, 5)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_adjacent_to() {
|
||||
assert!(!Range::new(1, 2).adjacent_to(Range::new(1, 1)));
|
||||
assert!(!Range::new(1, 2).adjacent_to(Range::new(2, 2)));
|
||||
assert!(Range::new(1, 2).adjacent_to(Range::new(0, 0)));
|
||||
assert!(Range::new(1, 2).adjacent_to(Range::new(3, 10)));
|
||||
assert!(!Range::new(1, 3).adjacent_to(Range::new(5, 5)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_contains() {
|
||||
assert!(Range::new(1, 2).contains(Range::new(1, 1)));
|
||||
assert!(Range::new(1, 2).contains(Range::new(2, 2)));
|
||||
assert!(!Range::new(1, 2).contains(Range::new(0, 0)));
|
||||
assert!(!Range::new(1, 2).contains(Range::new(3, 10)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_merge() {
|
||||
assert_eq!(None, Range::new(1, 3).merge(Range::new(5, 5)));
|
||||
assert_eq!(None, Range::new(4, 7).merge(Range::new(0, 1)));
|
||||
assert_eq!(Some(Range::new(3, 7)),
|
||||
Range::new(3, 5).merge(Range::new(4, 7)));
|
||||
assert_eq!(Some(Range::new(3, 7)),
|
||||
Range::new(3, 5).merge(Range::new(5, 7)));
|
||||
assert_eq!(Some(Range::new(3, 7)),
|
||||
Range::new(3, 5).merge(Range::new(6, 7)));
|
||||
assert_eq!(Some(Range::new(3, 7)),
|
||||
Range::new(3, 7).merge(Range::new(4, 5)));
|
||||
}
|
||||
}
|
@ -9,9 +9,9 @@
|
||||
// except according to those terms.
|
||||
|
||||
use Indent;
|
||||
use codemap::SpanUtils;
|
||||
use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic, definitive_tactic};
|
||||
use types::rewrite_path;
|
||||
use utils::CodeMapSpanUtils;
|
||||
use rewrite::{Rewrite, RewriteContext};
|
||||
|
||||
use syntax::ast;
|
||||
|
@ -11,8 +11,9 @@
|
||||
// Formatting top-level items - functions, structs, enums, traits, impls.
|
||||
|
||||
use Indent;
|
||||
use utils::{CodeMapSpanUtils, format_mutability, format_visibility, contains_skip, end_typaram,
|
||||
wrap_str, last_line_width, semicolon_for_expr, format_unsafety, trim_newlines};
|
||||
use codemap::SpanUtils;
|
||||
use utils::{format_mutability, format_visibility, contains_skip, end_typaram, wrap_str,
|
||||
last_line_width, semicolon_for_expr, format_unsafety, trim_newlines};
|
||||
use lists::{write_list, itemize_list, ListItem, ListFormatting, SeparatorTactic,
|
||||
DefinitiveListTactic, ListTactic, definitive_tactic, format_item_list};
|
||||
use expr::{is_empty_block, is_simple_block_stmt, rewrite_assign_rhs};
|
||||
|
@ -25,6 +25,7 @@ extern crate regex;
|
||||
extern crate diff;
|
||||
extern crate term;
|
||||
extern crate itertools;
|
||||
extern crate multimap;
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::codemap::{mk_sp, CodeMap, Span};
|
||||
@ -52,7 +53,9 @@ pub use self::summary::Summary;
|
||||
#[macro_use]
|
||||
mod utils;
|
||||
pub mod config;
|
||||
pub mod codemap;
|
||||
pub mod filemap;
|
||||
pub mod file_lines;
|
||||
pub mod visitor;
|
||||
mod checkstyle;
|
||||
mod items;
|
||||
@ -470,6 +473,7 @@ pub fn format_input<T: Write>(input: Input,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Input {
|
||||
File(PathBuf),
|
||||
Text(String),
|
||||
|
@ -25,10 +25,11 @@ use syntax::parse::tts_to_parser;
|
||||
use syntax::codemap::{mk_sp, BytePos};
|
||||
|
||||
use Indent;
|
||||
use codemap::SpanUtils;
|
||||
use rewrite::{Rewrite, RewriteContext};
|
||||
use expr::{rewrite_call, rewrite_array};
|
||||
use comment::{FindUncommented, contains_comment};
|
||||
use utils::{CodeMapSpanUtils, wrap_str};
|
||||
use utils::wrap_str;
|
||||
|
||||
const FORCED_BRACKET_MACROS: &'static [&'static str] = &["vec!"];
|
||||
|
||||
|
@ -9,8 +9,9 @@
|
||||
// except according to those terms.
|
||||
|
||||
use Indent;
|
||||
use codemap::SpanUtils;
|
||||
use rewrite::{Rewrite, RewriteContext};
|
||||
use utils::{CodeMapSpanUtils, wrap_str, format_mutability};
|
||||
use utils::{wrap_str, format_mutability};
|
||||
use lists::{format_item_list, itemize_list};
|
||||
use expr::{rewrite_unary_prefix, rewrite_pair, rewrite_tuple};
|
||||
use types::rewrite_path;
|
||||
|
@ -17,9 +17,10 @@ use syntax::codemap::{self, Span, BytePos};
|
||||
use syntax::abi;
|
||||
|
||||
use {Indent, Spanned};
|
||||
use codemap::SpanUtils;
|
||||
use lists::{format_item_list, itemize_list, format_fn_args};
|
||||
use rewrite::{Rewrite, RewriteContext};
|
||||
use utils::{CodeMapSpanUtils, extra_offset, format_mutability, wrap_str};
|
||||
use utils::{extra_offset, format_mutability, wrap_str};
|
||||
use expr::{rewrite_unary_prefix, rewrite_pair, rewrite_tuple};
|
||||
use config::TypeDensity;
|
||||
|
||||
|
39
src/utils.rs
39
src/utils.rs
@ -14,51 +14,14 @@ use std::cmp::Ordering;
|
||||
use itertools::Itertools;
|
||||
|
||||
use syntax::ast::{self, Visibility, Attribute, MetaItem, MetaItemKind, Path};
|
||||
use syntax::codemap::{CodeMap, Span, BytePos};
|
||||
use syntax::codemap::BytePos;
|
||||
use syntax::abi;
|
||||
|
||||
use Indent;
|
||||
use comment::FindUncommented;
|
||||
use rewrite::{Rewrite, RewriteContext};
|
||||
|
||||
use SKIP_ANNOTATION;
|
||||
|
||||
pub trait CodeMapSpanUtils {
|
||||
fn span_after(&self, original: Span, needle: &str) -> BytePos;
|
||||
fn span_after_last(&self, original: Span, needle: &str) -> BytePos;
|
||||
fn span_before(&self, original: Span, needle: &str) -> BytePos;
|
||||
}
|
||||
|
||||
impl CodeMapSpanUtils for CodeMap {
|
||||
#[inline]
|
||||
fn span_after(&self, original: Span, needle: &str) -> BytePos {
|
||||
let snippet = self.span_to_snippet(original).unwrap();
|
||||
let offset = snippet.find_uncommented(needle).unwrap() + needle.len();
|
||||
|
||||
original.lo + BytePos(offset as u32)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn span_after_last(&self, original: Span, needle: &str) -> BytePos {
|
||||
let snippet = self.span_to_snippet(original).unwrap();
|
||||
let mut offset = 0;
|
||||
|
||||
while let Some(additional_offset) = snippet[offset..].find_uncommented(needle) {
|
||||
offset += additional_offset + needle.len();
|
||||
}
|
||||
|
||||
original.lo + BytePos(offset as u32)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn span_before(&self, original: Span, needle: &str) -> BytePos {
|
||||
let snippet = self.span_to_snippet(original).unwrap();
|
||||
let offset = snippet.find_uncommented(needle).unwrap();
|
||||
|
||||
original.lo + BytePos(offset as u32)
|
||||
}
|
||||
}
|
||||
|
||||
// Computes the length of a string's last line, minus offset.
|
||||
#[inline]
|
||||
pub fn extra_offset(text: &str, offset: Indent) -> usize {
|
||||
|
@ -15,7 +15,8 @@ use syntax::parse::ParseSess;
|
||||
use strings::string_buffer::StringBuffer;
|
||||
|
||||
use Indent;
|
||||
use utils::{self, CodeMapSpanUtils};
|
||||
use utils;
|
||||
use codemap::{LineRangeUtils, SpanUtils};
|
||||
use config::Config;
|
||||
use rewrite::{Rewrite, RewriteContext};
|
||||
use comment::rewrite_comment;
|
||||
@ -42,6 +43,15 @@ pub struct FmtVisitor<'a> {
|
||||
|
||||
impl<'a> FmtVisitor<'a> {
|
||||
fn visit_stmt(&mut self, stmt: &ast::Stmt) {
|
||||
debug!("visit_stmt: {:?} {:?}",
|
||||
self.codemap.lookup_char_pos(stmt.span.lo),
|
||||
self.codemap.lookup_char_pos(stmt.span.hi));
|
||||
|
||||
// FIXME(#434): Move this check to somewhere more central, eg Rewrite.
|
||||
if !self.config.file_lines.contains(&self.codemap.lookup_line_range(stmt.span)) {
|
||||
return;
|
||||
}
|
||||
|
||||
match stmt.node {
|
||||
ast::StmtKind::Decl(ref decl, _) => {
|
||||
if let ast::DeclKind::Item(ref item) = decl.node {
|
||||
|
29
tests/source/file-lines-1.rs
Normal file
29
tests/source/file-lines-1.rs
Normal file
@ -0,0 +1,29 @@
|
||||
// rustfmt-file_lines: [{"file":"tests/source/file-lines-1.rs","range":[4,8]}]
|
||||
|
||||
fn floaters() {
|
||||
let x = Foo {
|
||||
field1: val1,
|
||||
field2: val2,
|
||||
}
|
||||
.method_call().method_call();
|
||||
|
||||
let y = if cond {
|
||||
val1
|
||||
} else {
|
||||
val2
|
||||
}
|
||||
.method_call();
|
||||
|
||||
{
|
||||
match x {
|
||||
PushParam => {
|
||||
// params are 1-indexed
|
||||
stack.push(mparams[match cur.to_digit(10) {
|
||||
Some(d) => d as usize - 1,
|
||||
None => return Err("bad param number".to_owned()),
|
||||
}]
|
||||
.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
29
tests/source/file-lines-2.rs
Normal file
29
tests/source/file-lines-2.rs
Normal file
@ -0,0 +1,29 @@
|
||||
// rustfmt-file_lines: [{"file":"tests/source/file-lines-2.rs","range":[10,15]}]
|
||||
|
||||
fn floaters() {
|
||||
let x = Foo {
|
||||
field1: val1,
|
||||
field2: val2,
|
||||
}
|
||||
.method_call().method_call();
|
||||
|
||||
let y = if cond {
|
||||
val1
|
||||
} else {
|
||||
val2
|
||||
}
|
||||
.method_call();
|
||||
|
||||
{
|
||||
match x {
|
||||
PushParam => {
|
||||
// params are 1-indexed
|
||||
stack.push(mparams[match cur.to_digit(10) {
|
||||
Some(d) => d as usize - 1,
|
||||
None => return Err("bad param number".to_owned()),
|
||||
}]
|
||||
.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
29
tests/source/file-lines-3.rs
Normal file
29
tests/source/file-lines-3.rs
Normal file
@ -0,0 +1,29 @@
|
||||
// rustfmt-file_lines: [{"file":"tests/source/file-lines-3.rs","range":[4,8]},{"file":"tests/source/file-lines-3.rs","range":[10,15]}]
|
||||
|
||||
fn floaters() {
|
||||
let x = Foo {
|
||||
field1: val1,
|
||||
field2: val2,
|
||||
}
|
||||
.method_call().method_call();
|
||||
|
||||
let y = if cond {
|
||||
val1
|
||||
} else {
|
||||
val2
|
||||
}
|
||||
.method_call();
|
||||
|
||||
{
|
||||
match x {
|
||||
PushParam => {
|
||||
// params are 1-indexed
|
||||
stack.push(mparams[match cur.to_digit(10) {
|
||||
Some(d) => d as usize - 1,
|
||||
None => return Err("bad param number".to_owned()),
|
||||
}]
|
||||
.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
30
tests/source/file-lines-4.rs
Normal file
30
tests/source/file-lines-4.rs
Normal file
@ -0,0 +1,30 @@
|
||||
// rustfmt-file_lines: []
|
||||
// (Test that nothing is formatted if an empty array is specified.)
|
||||
|
||||
fn floaters() {
|
||||
let x = Foo {
|
||||
field1: val1,
|
||||
field2: val2,
|
||||
}
|
||||
.method_call().method_call();
|
||||
|
||||
let y = if cond {
|
||||
val1
|
||||
} else {
|
||||
val2
|
||||
}
|
||||
.method_call();
|
||||
|
||||
{
|
||||
match x {
|
||||
PushParam => {
|
||||
// params are 1-indexed
|
||||
stack.push(mparams[match cur.to_digit(10) {
|
||||
Some(d) => d as usize - 1,
|
||||
None => return Err("bad param number".to_owned()),
|
||||
}]
|
||||
.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
30
tests/target/file-lines-1.rs
Normal file
30
tests/target/file-lines-1.rs
Normal file
@ -0,0 +1,30 @@
|
||||
// rustfmt-file_lines: [{"file":"tests/source/file-lines-1.rs","range":[4,8]}]
|
||||
|
||||
fn floaters() {
|
||||
let x = Foo {
|
||||
field1: val1,
|
||||
field2: val2,
|
||||
}
|
||||
.method_call()
|
||||
.method_call();
|
||||
|
||||
let y = if cond {
|
||||
val1
|
||||
} else {
|
||||
val2
|
||||
}
|
||||
.method_call();
|
||||
|
||||
{
|
||||
match x {
|
||||
PushParam => {
|
||||
// params are 1-indexed
|
||||
stack.push(mparams[match cur.to_digit(10) {
|
||||
Some(d) => d as usize - 1,
|
||||
None => return Err("bad param number".to_owned()),
|
||||
}]
|
||||
.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
24
tests/target/file-lines-2.rs
Normal file
24
tests/target/file-lines-2.rs
Normal file
@ -0,0 +1,24 @@
|
||||
// rustfmt-file_lines: [{"file":"tests/source/file-lines-2.rs","range":[10,15]}]
|
||||
|
||||
fn floaters() {
|
||||
let x = Foo {
|
||||
field1: val1,
|
||||
field2: val2,
|
||||
}
|
||||
.method_call().method_call();
|
||||
|
||||
let y = if cond { val1 } else { val2 }.method_call();
|
||||
|
||||
{
|
||||
match x {
|
||||
PushParam => {
|
||||
// params are 1-indexed
|
||||
stack.push(mparams[match cur.to_digit(10) {
|
||||
Some(d) => d as usize - 1,
|
||||
None => return Err("bad param number".to_owned()),
|
||||
}]
|
||||
.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
25
tests/target/file-lines-3.rs
Normal file
25
tests/target/file-lines-3.rs
Normal file
@ -0,0 +1,25 @@
|
||||
// rustfmt-file_lines: [{"file":"tests/source/file-lines-3.rs","range":[4,8]},{"file":"tests/source/file-lines-3.rs","range":[10,15]}]
|
||||
|
||||
fn floaters() {
|
||||
let x = Foo {
|
||||
field1: val1,
|
||||
field2: val2,
|
||||
}
|
||||
.method_call()
|
||||
.method_call();
|
||||
|
||||
let y = if cond { val1 } else { val2 }.method_call();
|
||||
|
||||
{
|
||||
match x {
|
||||
PushParam => {
|
||||
// params are 1-indexed
|
||||
stack.push(mparams[match cur.to_digit(10) {
|
||||
Some(d) => d as usize - 1,
|
||||
None => return Err("bad param number".to_owned()),
|
||||
}]
|
||||
.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
30
tests/target/file-lines-4.rs
Normal file
30
tests/target/file-lines-4.rs
Normal file
@ -0,0 +1,30 @@
|
||||
// rustfmt-file_lines: []
|
||||
// (Test that nothing is formatted if an empty array is specified.)
|
||||
|
||||
fn floaters() {
|
||||
let x = Foo {
|
||||
field1: val1,
|
||||
field2: val2,
|
||||
}
|
||||
.method_call().method_call();
|
||||
|
||||
let y = if cond {
|
||||
val1
|
||||
} else {
|
||||
val2
|
||||
}
|
||||
.method_call();
|
||||
|
||||
{
|
||||
match x {
|
||||
PushParam => {
|
||||
// params are 1-indexed
|
||||
stack.push(mparams[match cur.to_digit(10) {
|
||||
Some(d) => d as usize - 1,
|
||||
None => return Err("bad param number".to_owned()),
|
||||
}]
|
||||
.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user