Skip to content

Commit f80dcbb

Browse files
committedSep 7, 2015
Split off binary search
1 parent d05a41c commit f80dcbb

File tree

4 files changed

+98
-57
lines changed

4 files changed

+98
-57
lines changed
 

‎src/expr.rs

+17-28
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ use rewrite::{Rewrite, RewriteContext};
1414
use lists::{write_list, itemize_list, ListFormatting, SeparatorTactic, ListTactic};
1515
use string::{StringFormat, rewrite_string};
1616
use StructLitStyle;
17-
use utils::{span_after, make_indent, extra_offset, first_line_width, last_line_width, wrap_str};
17+
use utils::{span_after, make_indent, extra_offset, first_line_width, last_line_width, wrap_str,
18+
binary_search};
1819
use visitor::FmtVisitor;
1920
use config::BlockIndentStyle;
2021
use comment::{FindUncommented, rewrite_comment, contains_comment};
@@ -842,31 +843,19 @@ fn rewrite_call(context: &RewriteContext,
842843
width: usize,
843844
offset: usize)
844845
-> Option<String> {
845-
debug!("rewrite_call, width: {}, offset: {}", width, offset);
846+
let callback = |callee_max_width| {
847+
rewrite_call_inner(context,
848+
callee,
849+
callee_max_width,
850+
args,
851+
span,
852+
width,
853+
offset)
854+
};
846855

847856
// 2 is for parens
848-
let mut hi = try_opt!(width.checked_sub(2)) * 2;
849-
let mut lo = 0;
850-
let mut tries = 0;
851-
852-
// Binary search for the best split between callee and arguments.
853-
loop {
854-
let middle = (lo + hi) / 2;
855-
856-
match rewrite_call_inner(context, callee, middle, args, span, width, offset) {
857-
_ if tries > 10 => return None,
858-
Ok(result) => return Some(result),
859-
Err(Ordering::Less) => {
860-
lo = middle;
861-
tries += 1;
862-
}
863-
Err(Ordering::Greater) => {
864-
hi = middle;
865-
tries += 1;
866-
}
867-
_ => unreachable!(),
868-
}
869-
}
857+
let max_width = try_opt!(width.checked_sub(2));
858+
binary_search(1, max_width, callback)
Has a comment. Original line has a comment.
870859
}
871860

872861
fn rewrite_call_inner(context: &RewriteContext,
@@ -877,7 +866,8 @@ fn rewrite_call_inner(context: &RewriteContext,
877866
width: usize,
878867
offset: usize)
879868
-> Result<String, Ordering> {
880-
// FIXME using byte lens instead of char lens (and probably all over the place too)
869+
// FIXME using byte lens instead of char lens (and probably all over the
870+
// place too)
881871
let callee_str = match callee.rewrite(context, max_callee_width, offset) {
882872
Some(string) => {
883873
if !string.contains('\n') && string.len() > max_callee_width {
@@ -886,9 +876,8 @@ fn rewrite_call_inner(context: &RewriteContext,
886876
string
887877
}
888878
}
889-
None => return Err(Ordering::Less),
879+
None => return Err(Ordering::Greater),
890880
};
891-
debug!("rewrite_call, callee_str: `{}`", callee_str);
892881

893882
let extra_offset = extra_offset(&callee_str, offset);
894883
// 2 is for parens.
@@ -916,7 +905,7 @@ fn rewrite_call_inner(context: &RewriteContext,
916905
let fmt = ListFormatting::for_fn(remaining_width, offset);
917906
let list_str = match write_list(&items.collect::<Vec<_>>(), &fmt) {
918907
Some(str) => str,
919-
None => return Err(Ordering::Greater),
908+
None => return Err(Ordering::Less),
920909
};
921910

922911
Ok(format!("{}({})", callee_str, list_str))

‎src/lists.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,8 @@ pub fn write_list<'b>(items: &[ListItem], formatting: &ListFormatting<'b>) -> Op
204204
}
205205
}
206206

207-
// FIXME: no magic numbers!
208-
let item_str = wrap_str(&item.item[..], 100, formatting.v_width, formatting.indent);
207+
let max_width = formatting.indent + formatting.v_width;
208+
let item_str = wrap_str(&item.item[..], max_width, formatting.v_width, formatting.indent);
209209
result.push_str(&&try_opt!(item_str));
210210

211211
// Post-comments

‎src/types.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -218,17 +218,22 @@ fn rewrite_segment(segment: &ast::PathSegment,
218218
">",
219219
|param| param.get_span().lo,
220220
|param| param.get_span().hi,
221-
// FIXME: need better params
221+
// FIXME(#133): write_list should call
222+
// rewrite itself, because it has a better
223+
// context.
222224
|seg| {
223-
seg.rewrite(context, 1000, offset + extra_offset).unwrap()
225+
seg.rewrite(context,
226+
context.config.max_width,
227+
offset + extra_offset)
228+
.unwrap()
224229
},
225230
list_lo,
226231
span_hi);
227232

228233
let fmt = ListFormatting::for_fn(list_width, offset + extra_offset);
229234
let list_str = try_opt!(write_list(&items.collect::<Vec<_>>(), &fmt));
230235

231-
// update pos
236+
// Update position of last bracket.
232237
*span_lo = next_span_lo;
233238

234239
format!("{}<{}>", separator, list_str)
@@ -256,9 +261,6 @@ fn rewrite_segment(segment: &ast::PathSegment,
256261
let fmt = ListFormatting::for_fn(budget, offset + 1);
257262
let list_str = try_opt!(write_list(&items.collect::<Vec<_>>(), &fmt));
258263

259-
// update pos
260-
*span_lo = data.span.hi + BytePos(1);
261-
262264
format!("({}){}", list_str, output)
263265
}
264266
_ => String::new(),

‎src/utils.rs

+71-21
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use std::cmp::Ordering;
12+
1113
use syntax::ast::{self, Visibility, Attribute, MetaItem, MetaItem_};
1214
use syntax::codemap::{CodeMap, Span, BytePos};
1315

@@ -164,38 +166,86 @@ macro_rules! try_opt {
164166
})
165167
}
166168

169+
// Wraps string-like values in an Option. Returns Some when the string adheres
170+
// to the Rewrite constraints defined for the Rewrite trait and else otherwise.
167171
pub fn wrap_str<S: AsRef<str>>(s: S, max_width: usize, width: usize, offset: usize) -> Option<S> {
168-
let snippet = s.as_ref();
169-
170-
if !snippet.contains('\n') && snippet.len() > width {
171-
return None;
172-
} else {
173-
let mut lines = snippet.lines();
172+
{
173+
let snippet = s.as_ref();
174174

175-
// The caller of this function has already placed `offset`
176-
// characters on the first line.
177-
let first_line_max_len = try_opt!(max_width.checked_sub(offset));
178-
if lines.next().unwrap().len() > first_line_max_len {
175+
if !snippet.contains('\n') && snippet.len() > width {
179176
return None;
180-
}
177+
} else {
178+
let mut lines = snippet.lines();
179+
180+
// The caller of this function has already placed `offset`
181+
// characters on the first line.
182+
let first_line_max_len = try_opt!(max_width.checked_sub(offset));
183+
if lines.next().unwrap().len() > first_line_max_len {
184+
return None;
185+
}
181186

182-
// The other lines must fit within the maximum width.
183-
if lines.find(|line| line.len() > max_width).is_some() {
184-
return None;
185-
}
187+
// The other lines must fit within the maximum width.
188+
if lines.find(|line| line.len() > max_width).is_some() {
189+
return None;
190+
}
186191

187-
// `width` is the maximum length of the last line, excluding
188-
// indentation.
189-
// A special check for the last line, since the caller may
190-
// place trailing characters on this line.
191-
if snippet.lines().rev().next().unwrap().len() > offset + width {
192-
return None;
192+
// `width` is the maximum length of the last line, excluding
193+
// indentation.
194+
// A special check for the last line, since the caller may
195+
// place trailing characters on this line.
196+
if snippet.lines().rev().next().unwrap().len() > offset + width {
197+
return None;
198+
}
193199
}
194200
}
195201

196202
Some(s)
197203
}
198204

205+
// Binary search in integer range. Returns the first Ok value returned by the
206+
// callback.
207+
// The callback takes an integer and returns either an Ok, or an Err indicating
208+
// whether the `guess' was too high (Ordering::Less), or too low.
209+
// This function is guaranteed to try to the hi value first.
210+
pub fn binary_search<C, T>(mut lo: usize, mut hi: usize, callback: C) -> Option<T>
211+
where C: Fn(usize) -> Result<T, Ordering>
212+
{
213+
let mut middle = hi;
214+
215+
while lo <= hi {
216+
match callback(middle) {
217+
Ok(val) => return Some(val),
218+
Err(Ordering::Less) => {
219+
hi = middle - 1;
220+
}
221+
Err(..) => {
222+
lo = middle + 1;
223+
}
224+
}
225+
middle = (hi + lo) / 2;
226+
}
227+
228+
None
229+
}
230+
231+
#[test]
232+
fn bin_search_test() {
233+
let closure = |i| {
234+
match i {
235+
4 => Ok(()),
236+
j if j > 4 => Err(Ordering::Less),
237+
j if j < 4 => Err(Ordering::Greater),
238+
_ => unreachable!(),
239+
}
240+
};
241+
242+
assert_eq!(Some(()), binary_search(1, 10, &closure));
243+
assert_eq!(None, binary_search(1, 3, &closure));
244+
assert_eq!(Some(()), binary_search(0, 44, &closure));
245+
assert_eq!(Some(()), binary_search(4, 125, &closure));
246+
assert_eq!(None, binary_search(6, 100, &closure));
247+
}
248+
199249
#[test]
200250
fn power_rounding() {
201251
assert_eq!(0, round_up_to_power_of_two(0));

0 commit comments

Comments
 (0)
Please sign in to comment.