aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/introduce_variable.rs5
-rw-r--r--crates/ra_lsp_server/Cargo.toml2
-rw-r--r--crates/ra_lsp_server/src/conv.rs150
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs6
-rw-r--r--crates/ra_lsp_server/src/world.rs6
-rw-r--r--crates/ra_lsp_server/tests/heavy_tests/main.rs48
-rw-r--r--crates/ra_syntax/Cargo.toml2
-rw-r--r--crates/ra_syntax/src/parsing/lexer.rs136
-rw-r--r--crates/ra_syntax/src/validation.rs54
-rw-r--r--crates/test_utils/src/lib.rs22
10 files changed, 212 insertions, 219 deletions
diff --git a/crates/ra_assists/src/introduce_variable.rs b/crates/ra_assists/src/introduce_variable.rs
index 5eb708310..95c18d0e3 100644
--- a/crates/ra_assists/src/introduce_variable.rs
+++ b/crates/ra_assists/src/introduce_variable.rs
@@ -56,10 +56,7 @@ pub(crate) fn introduce_variable(mut ctx: AssistCtx<impl HirDatabase>) -> Option
56 // but we do not want to duplicate possible 56 // but we do not want to duplicate possible
57 // extra newlines in the indent block 57 // extra newlines in the indent block
58 let text = indent.text(); 58 let text = indent.text();
59 if text.starts_with("\r\n") { 59 if text.starts_with('\n') {
60 buf.push_str("\r\n");
61 buf.push_str(text.trim_start_matches("\r\n"));
62 } else if text.starts_with('\n') {
63 buf.push_str("\n"); 60 buf.push_str("\n");
64 buf.push_str(text.trim_start_matches('\n')); 61 buf.push_str(text.trim_start_matches('\n'));
65 } else { 62 } else {
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml
index 2c69a6993..afeac0d8a 100644
--- a/crates/ra_lsp_server/Cargo.toml
+++ b/crates/ra_lsp_server/Cargo.toml
@@ -16,7 +16,7 @@ lsp-types = { version = "0.60.0", features = ["proposed"] }
16rustc-hash = "1.0" 16rustc-hash = "1.0"
17parking_lot = "0.9.0" 17parking_lot = "0.9.0"
18 18
19ra_vfs = "0.2.0" 19ra_vfs = "0.2.7"
20thread_worker = { path = "../thread_worker" } 20thread_worker = { path = "../thread_worker" }
21ra_syntax = { path = "../ra_syntax" } 21ra_syntax = { path = "../ra_syntax" }
22ra_text_edit = { path = "../ra_text_edit" } 22ra_text_edit = { path = "../ra_text_edit" }
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs
index df8ea6e0d..0ad2ea10f 100644
--- a/crates/ra_lsp_server/src/conv.rs
+++ b/crates/ra_lsp_server/src/conv.rs
@@ -11,6 +11,7 @@ use ra_ide_api::{
11}; 11};
12use ra_syntax::{SyntaxKind, TextRange, TextUnit}; 12use ra_syntax::{SyntaxKind, TextRange, TextUnit};
13use ra_text_edit::{AtomTextEdit, TextEdit}; 13use ra_text_edit::{AtomTextEdit, TextEdit};
14use ra_vfs::LineEndings;
14 15
15use crate::{req, world::WorldSnapshot, Result}; 16use crate::{req, world::WorldSnapshot, Result};
16 17
@@ -19,16 +20,14 @@ pub trait Conv {
19 fn conv(self) -> Self::Output; 20 fn conv(self) -> Self::Output;
20} 21}
21 22
22pub trait ConvWith { 23pub trait ConvWith<CTX> {
23 type Ctx;
24 type Output; 24 type Output;
25 fn conv_with(self, ctx: &Self::Ctx) -> Self::Output; 25 fn conv_with(self, ctx: CTX) -> Self::Output;
26} 26}
27 27
28pub trait TryConvWith { 28pub trait TryConvWith<CTX> {
29 type Ctx;
30 type Output; 29 type Output;
31 fn try_conv_with(self, ctx: &Self::Ctx) -> Result<Self::Output>; 30 fn try_conv_with(self, ctx: CTX) -> Result<Self::Output>;
32} 31}
33 32
34impl Conv for SyntaxKind { 33impl Conv for SyntaxKind {
@@ -89,11 +88,10 @@ impl Conv for Severity {
89 } 88 }
90} 89}
91 90
92impl ConvWith for CompletionItem { 91impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem {
93 type Ctx = LineIndex;
94 type Output = ::lsp_types::CompletionItem; 92 type Output = ::lsp_types::CompletionItem;
95 93
96 fn conv_with(self, ctx: &LineIndex) -> ::lsp_types::CompletionItem { 94 fn conv_with(self, ctx: (&LineIndex, LineEndings)) -> ::lsp_types::CompletionItem {
97 let mut additional_text_edits = Vec::new(); 95 let mut additional_text_edits = Vec::new();
98 let mut text_edit = None; 96 let mut text_edit = None;
99 // LSP does not allow arbitrary edits in completion, so we have to do a 97 // LSP does not allow arbitrary edits in completion, so we have to do a
@@ -138,8 +136,7 @@ impl ConvWith for CompletionItem {
138 } 136 }
139} 137}
140 138
141impl ConvWith for Position { 139impl ConvWith<&LineIndex> for Position {
142 type Ctx = LineIndex;
143 type Output = TextUnit; 140 type Output = TextUnit;
144 141
145 fn conv_with(self, line_index: &LineIndex) -> TextUnit { 142 fn conv_with(self, line_index: &LineIndex) -> TextUnit {
@@ -148,8 +145,7 @@ impl ConvWith for Position {
148 } 145 }
149} 146}
150 147
151impl ConvWith for TextUnit { 148impl ConvWith<&LineIndex> for TextUnit {
152 type Ctx = LineIndex;
153 type Output = Position; 149 type Output = Position;
154 150
155 fn conv_with(self, line_index: &LineIndex) -> Position { 151 fn conv_with(self, line_index: &LineIndex) -> Position {
@@ -158,8 +154,7 @@ impl ConvWith for TextUnit {
158 } 154 }
159} 155}
160 156
161impl ConvWith for TextRange { 157impl ConvWith<&LineIndex> for TextRange {
162 type Ctx = LineIndex;
163 type Output = Range; 158 type Output = Range;
164 159
165 fn conv_with(self, line_index: &LineIndex) -> Range { 160 fn conv_with(self, line_index: &LineIndex) -> Range {
@@ -167,8 +162,7 @@ impl ConvWith for TextRange {
167 } 162 }
168} 163}
169 164
170impl ConvWith for Range { 165impl ConvWith<&LineIndex> for Range {
171 type Ctx = LineIndex;
172 type Output = TextRange; 166 type Output = TextRange;
173 167
174 fn conv_with(self, line_index: &LineIndex) -> TextRange { 168 fn conv_with(self, line_index: &LineIndex) -> TextRange {
@@ -208,77 +202,73 @@ impl Conv for ra_ide_api::FunctionSignature {
208 } 202 }
209} 203}
210 204
211impl ConvWith for TextEdit { 205impl ConvWith<(&LineIndex, LineEndings)> for TextEdit {
212 type Ctx = LineIndex;
213 type Output = Vec<lsp_types::TextEdit>; 206 type Output = Vec<lsp_types::TextEdit>;
214 207
215 fn conv_with(self, line_index: &LineIndex) -> Vec<lsp_types::TextEdit> { 208 fn conv_with(self, ctx: (&LineIndex, LineEndings)) -> Vec<lsp_types::TextEdit> {
216 self.as_atoms().iter().map_conv_with(line_index).collect() 209 self.as_atoms().iter().map_conv_with(ctx).collect()
217 } 210 }
218} 211}
219 212
220impl<'a> ConvWith for &'a AtomTextEdit { 213impl ConvWith<(&LineIndex, LineEndings)> for &AtomTextEdit {
221 type Ctx = LineIndex;
222 type Output = lsp_types::TextEdit; 214 type Output = lsp_types::TextEdit;
223 215
224 fn conv_with(self, line_index: &LineIndex) -> lsp_types::TextEdit { 216 fn conv_with(
225 lsp_types::TextEdit { 217 self,
226 range: self.delete.conv_with(line_index), 218 (line_index, line_endings): (&LineIndex, LineEndings),
227 new_text: self.insert.clone(), 219 ) -> lsp_types::TextEdit {
220 let mut new_text = self.insert.clone();
221 if line_endings == LineEndings::Dos {
222 new_text = new_text.replace('\n', "\r\n");
228 } 223 }
224 lsp_types::TextEdit { range: self.delete.conv_with(line_index), new_text }
229 } 225 }
230} 226}
231 227
232impl<T: ConvWith> ConvWith for Option<T> { 228impl<T: ConvWith<CTX>, CTX> ConvWith<CTX> for Option<T> {
233 type Ctx = <T as ConvWith>::Ctx; 229 type Output = Option<T::Output>;
234 type Output = Option<<T as ConvWith>::Output>; 230
235 fn conv_with(self, ctx: &Self::Ctx) -> Self::Output { 231 fn conv_with(self, ctx: CTX) -> Self::Output {
236 self.map(|x| ConvWith::conv_with(x, ctx)) 232 self.map(|x| ConvWith::conv_with(x, ctx))
237 } 233 }
238} 234}
239 235
240impl<'a> TryConvWith for &'a Url { 236impl TryConvWith<&WorldSnapshot> for &Url {
241 type Ctx = WorldSnapshot;
242 type Output = FileId; 237 type Output = FileId;
243 fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> { 238 fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> {
244 world.uri_to_file_id(self) 239 world.uri_to_file_id(self)
245 } 240 }
246} 241}
247 242
248impl TryConvWith for FileId { 243impl TryConvWith<&WorldSnapshot> for FileId {
249 type Ctx = WorldSnapshot;
250 type Output = Url; 244 type Output = Url;
251 fn try_conv_with(self, world: &WorldSnapshot) -> Result<Url> { 245 fn try_conv_with(self, world: &WorldSnapshot) -> Result<Url> {
252 world.file_id_to_uri(self) 246 world.file_id_to_uri(self)
253 } 247 }
254} 248}
255 249
256impl<'a> TryConvWith for &'a TextDocumentItem { 250impl TryConvWith<&WorldSnapshot> for &TextDocumentItem {
257 type Ctx = WorldSnapshot;
258 type Output = FileId; 251 type Output = FileId;
259 fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> { 252 fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> {
260 self.uri.try_conv_with(world) 253 self.uri.try_conv_with(world)
261 } 254 }
262} 255}
263 256
264impl<'a> TryConvWith for &'a VersionedTextDocumentIdentifier { 257impl TryConvWith<&WorldSnapshot> for &VersionedTextDocumentIdentifier {
265 type Ctx = WorldSnapshot;
266 type Output = FileId; 258 type Output = FileId;
267 fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> { 259 fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> {
268 self.uri.try_conv_with(world) 260 self.uri.try_conv_with(world)
269 } 261 }
270} 262}
271 263
272impl<'a> TryConvWith for &'a TextDocumentIdentifier { 264impl TryConvWith<&WorldSnapshot> for &TextDocumentIdentifier {
273 type Ctx = WorldSnapshot;
274 type Output = FileId; 265 type Output = FileId;
275 fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> { 266 fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileId> {
276 world.uri_to_file_id(&self.uri) 267 world.uri_to_file_id(&self.uri)
277 } 268 }
278} 269}
279 270
280impl<'a> TryConvWith for &'a TextDocumentPositionParams { 271impl TryConvWith<&WorldSnapshot> for &TextDocumentPositionParams {
281 type Ctx = WorldSnapshot;
282 type Output = FilePosition; 272 type Output = FilePosition;
283 fn try_conv_with(self, world: &WorldSnapshot) -> Result<FilePosition> { 273 fn try_conv_with(self, world: &WorldSnapshot) -> Result<FilePosition> {
284 let file_id = self.text_document.try_conv_with(world)?; 274 let file_id = self.text_document.try_conv_with(world)?;
@@ -288,8 +278,7 @@ impl<'a> TryConvWith for &'a TextDocumentPositionParams {
288 } 278 }
289} 279}
290 280
291impl<'a> TryConvWith for (&'a TextDocumentIdentifier, Range) { 281impl TryConvWith<&WorldSnapshot> for (&TextDocumentIdentifier, Range) {
292 type Ctx = WorldSnapshot;
293 type Output = FileRange; 282 type Output = FileRange;
294 fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileRange> { 283 fn try_conv_with(self, world: &WorldSnapshot) -> Result<FileRange> {
295 let file_id = self.0.try_conv_with(world)?; 284 let file_id = self.0.try_conv_with(world)?;
@@ -299,10 +288,9 @@ impl<'a> TryConvWith for (&'a TextDocumentIdentifier, Range) {
299 } 288 }
300} 289}
301 290
302impl<T: TryConvWith> TryConvWith for Vec<T> { 291impl<T: TryConvWith<CTX>, CTX: Copy> TryConvWith<CTX> for Vec<T> {
303 type Ctx = <T as TryConvWith>::Ctx; 292 type Output = Vec<<T as TryConvWith<CTX>>::Output>;
304 type Output = Vec<<T as TryConvWith>::Output>; 293 fn try_conv_with(self, ctx: CTX) -> Result<Self::Output> {
305 fn try_conv_with(self, ctx: &Self::Ctx) -> Result<Self::Output> {
306 let mut res = Vec::with_capacity(self.len()); 294 let mut res = Vec::with_capacity(self.len());
307 for item in self { 295 for item in self {
308 res.push(item.try_conv_with(ctx)?); 296 res.push(item.try_conv_with(ctx)?);
@@ -311,8 +299,7 @@ impl<T: TryConvWith> TryConvWith for Vec<T> {
311 } 299 }
312} 300}
313 301
314impl TryConvWith for SourceChange { 302impl TryConvWith<&WorldSnapshot> for SourceChange {
315 type Ctx = WorldSnapshot;
316 type Output = req::SourceChange; 303 type Output = req::SourceChange;
317 fn try_conv_with(self, world: &WorldSnapshot) -> Result<req::SourceChange> { 304 fn try_conv_with(self, world: &WorldSnapshot) -> Result<req::SourceChange> {
318 let cursor_position = match self.cursor_position { 305 let cursor_position = match self.cursor_position {
@@ -351,8 +338,7 @@ impl TryConvWith for SourceChange {
351 } 338 }
352} 339}
353 340
354impl TryConvWith for SourceFileEdit { 341impl TryConvWith<&WorldSnapshot> for SourceFileEdit {
355 type Ctx = WorldSnapshot;
356 type Output = TextDocumentEdit; 342 type Output = TextDocumentEdit;
357 fn try_conv_with(self, world: &WorldSnapshot) -> Result<TextDocumentEdit> { 343 fn try_conv_with(self, world: &WorldSnapshot) -> Result<TextDocumentEdit> {
358 let text_document = VersionedTextDocumentIdentifier { 344 let text_document = VersionedTextDocumentIdentifier {
@@ -360,13 +346,14 @@ impl TryConvWith for SourceFileEdit {
360 version: None, 346 version: None,
361 }; 347 };
362 let line_index = world.analysis().file_line_index(self.file_id)?; 348 let line_index = world.analysis().file_line_index(self.file_id)?;
363 let edits = self.edit.as_atoms().iter().map_conv_with(&line_index).collect(); 349 let line_endings = world.file_line_endings(self.file_id);
350 let edits =
351 self.edit.as_atoms().iter().map_conv_with((&line_index, line_endings)).collect();
364 Ok(TextDocumentEdit { text_document, edits }) 352 Ok(TextDocumentEdit { text_document, edits })
365 } 353 }
366} 354}
367 355
368impl TryConvWith for FileSystemEdit { 356impl TryConvWith<&WorldSnapshot> for FileSystemEdit {
369 type Ctx = WorldSnapshot;
370 type Output = ResourceOp; 357 type Output = ResourceOp;
371 fn try_conv_with(self, world: &WorldSnapshot) -> Result<ResourceOp> { 358 fn try_conv_with(self, world: &WorldSnapshot) -> Result<ResourceOp> {
372 let res = match self { 359 let res = match self {
@@ -384,8 +371,7 @@ impl TryConvWith for FileSystemEdit {
384 } 371 }
385} 372}
386 373
387impl TryConvWith for &NavigationTarget { 374impl TryConvWith<&WorldSnapshot> for &NavigationTarget {
388 type Ctx = WorldSnapshot;
389 type Output = Location; 375 type Output = Location;
390 fn try_conv_with(self, world: &WorldSnapshot) -> Result<Location> { 376 fn try_conv_with(self, world: &WorldSnapshot) -> Result<Location> {
391 let line_index = world.analysis().file_line_index(self.file_id())?; 377 let line_index = world.analysis().file_line_index(self.file_id())?;
@@ -394,8 +380,7 @@ impl TryConvWith for &NavigationTarget {
394 } 380 }
395} 381}
396 382
397impl TryConvWith for (FileId, RangeInfo<NavigationTarget>) { 383impl TryConvWith<&WorldSnapshot> for (FileId, RangeInfo<NavigationTarget>) {
398 type Ctx = WorldSnapshot;
399 type Output = LocationLink; 384 type Output = LocationLink;
400 fn try_conv_with(self, world: &WorldSnapshot) -> Result<LocationLink> { 385 fn try_conv_with(self, world: &WorldSnapshot) -> Result<LocationLink> {
401 let (src_file_id, target) = self; 386 let (src_file_id, target) = self;
@@ -422,8 +407,7 @@ impl TryConvWith for (FileId, RangeInfo<NavigationTarget>) {
422 } 407 }
423} 408}
424 409
425impl TryConvWith for (FileId, RangeInfo<Vec<NavigationTarget>>) { 410impl TryConvWith<&WorldSnapshot> for (FileId, RangeInfo<Vec<NavigationTarget>>) {
426 type Ctx = WorldSnapshot;
427 type Output = req::GotoDefinitionResponse; 411 type Output = req::GotoDefinitionResponse;
428 fn try_conv_with(self, world: &WorldSnapshot) -> Result<req::GotoTypeDefinitionResponse> { 412 fn try_conv_with(self, world: &WorldSnapshot) -> Result<req::GotoTypeDefinitionResponse> {
429 let (file_id, RangeInfo { range, info: navs }) = self; 413 let (file_id, RangeInfo { range, info: navs }) = self;
@@ -454,57 +438,55 @@ pub fn to_location(
454 Ok(loc) 438 Ok(loc)
455} 439}
456 440
457pub trait MapConvWith<'a>: Sized + 'a { 441pub trait MapConvWith<CTX>: Sized {
458 type Ctx;
459 type Output; 442 type Output;
460 443
461 fn map_conv_with(self, ctx: &'a Self::Ctx) -> ConvWithIter<'a, Self, Self::Ctx> { 444 fn map_conv_with(self, ctx: CTX) -> ConvWithIter<Self, CTX> {
462 ConvWithIter { iter: self, ctx } 445 ConvWithIter { iter: self, ctx }
463 } 446 }
464} 447}
465 448
466impl<'a, I> MapConvWith<'a> for I 449impl<CTX, I> MapConvWith<CTX> for I
467where 450where
468 I: Iterator + 'a, 451 I: Iterator,
469 I::Item: ConvWith, 452 I::Item: ConvWith<CTX>,
470{ 453{
471 type Ctx = <I::Item as ConvWith>::Ctx; 454 type Output = <I::Item as ConvWith<CTX>>::Output;
472 type Output = <I::Item as ConvWith>::Output;
473} 455}
474 456
475pub struct ConvWithIter<'a, I, Ctx: 'a> { 457pub struct ConvWithIter<I, CTX> {
476 iter: I, 458 iter: I,
477 ctx: &'a Ctx, 459 ctx: CTX,
478} 460}
479 461
480impl<'a, I, Ctx> Iterator for ConvWithIter<'a, I, Ctx> 462impl<I, CTX> Iterator for ConvWithIter<I, CTX>
481where 463where
482 I: Iterator, 464 I: Iterator,
483 I::Item: ConvWith<Ctx = Ctx>, 465 I::Item: ConvWith<CTX>,
466 CTX: Copy,
484{ 467{
485 type Item = <I::Item as ConvWith>::Output; 468 type Item = <I::Item as ConvWith<CTX>>::Output;
486 469
487 fn next(&mut self) -> Option<Self::Item> { 470 fn next(&mut self) -> Option<Self::Item> {
488 self.iter.next().map(|item| item.conv_with(self.ctx)) 471 self.iter.next().map(|item| item.conv_with(self.ctx))
489 } 472 }
490} 473}
491 474
492pub trait TryConvWithToVec<'a>: Sized + 'a { 475pub trait TryConvWithToVec<CTX>: Sized {
493 type Ctx;
494 type Output; 476 type Output;
495 477
496 fn try_conv_with_to_vec(self, ctx: &'a Self::Ctx) -> Result<Vec<Self::Output>>; 478 fn try_conv_with_to_vec(self, ctx: CTX) -> Result<Vec<Self::Output>>;
497} 479}
498 480
499impl<'a, I> TryConvWithToVec<'a> for I 481impl<I, CTX> TryConvWithToVec<CTX> for I
500where 482where
501 I: Iterator + 'a, 483 I: Iterator,
502 I::Item: TryConvWith, 484 I::Item: TryConvWith<CTX>,
485 CTX: Copy,
503{ 486{
504 type Ctx = <I::Item as TryConvWith>::Ctx; 487 type Output = <I::Item as TryConvWith<CTX>>::Output;
505 type Output = <I::Item as TryConvWith>::Output;
506 488
507 fn try_conv_with_to_vec(self, ctx: &'a Self::Ctx) -> Result<Vec<Self::Output>> { 489 fn try_conv_with_to_vec(self, ctx: CTX) -> Result<Vec<Self::Output>> {
508 self.map(|it| it.try_conv_with(ctx)).collect() 490 self.map(|it| it.try_conv_with(ctx)).collect()
509 } 491 }
510} 492}
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index b465707f8..3a559e845 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -138,6 +138,7 @@ pub fn handle_on_type_formatting(
138 let _p = profile("handle_on_type_formatting"); 138 let _p = profile("handle_on_type_formatting");
139 let mut position = params.text_document_position.try_conv_with(&world)?; 139 let mut position = params.text_document_position.try_conv_with(&world)?;
140 let line_index = world.analysis().file_line_index(position.file_id)?; 140 let line_index = world.analysis().file_line_index(position.file_id)?;
141 let line_endings = world.file_line_endings(position.file_id);
141 142
142 // in `ra_ide_api`, the `on_type` invariant is that 143 // in `ra_ide_api`, the `on_type` invariant is that
143 // `text.char_at(position) == typed_char`. 144 // `text.char_at(position) == typed_char`.
@@ -156,7 +157,7 @@ pub fn handle_on_type_formatting(
156 // This should be a single-file edit 157 // This should be a single-file edit
157 let edit = edit.source_file_edits.pop().unwrap(); 158 let edit = edit.source_file_edits.pop().unwrap();
158 159
159 let change: Vec<TextEdit> = edit.edit.conv_with(&line_index); 160 let change: Vec<TextEdit> = edit.edit.conv_with((&line_index, line_endings));
160 Ok(Some(change)) 161 Ok(Some(change))
161} 162}
162 163
@@ -370,8 +371,9 @@ pub fn handle_completion(
370 Some(items) => items, 371 Some(items) => items,
371 }; 372 };
372 let line_index = world.analysis().file_line_index(position.file_id)?; 373 let line_index = world.analysis().file_line_index(position.file_id)?;
374 let line_endings = world.file_line_endings(position.file_id);
373 let items: Vec<CompletionItem> = 375 let items: Vec<CompletionItem> =
374 items.into_iter().map(|item| item.conv_with(&line_index)).collect(); 376 items.into_iter().map(|item| item.conv_with((&line_index, line_endings))).collect();
375 377
376 Ok(Some(items.into())) 378 Ok(Some(items.into()))
377} 379}
diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs
index 9990ef62e..10f96812f 100644
--- a/crates/ra_lsp_server/src/world.rs
+++ b/crates/ra_lsp_server/src/world.rs
@@ -9,7 +9,7 @@ use parking_lot::RwLock;
9use ra_ide_api::{ 9use ra_ide_api::{
10 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId, 10 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId,
11}; 11};
12use ra_vfs::{RootEntry, Vfs, VfsChange, VfsFile, VfsRoot}; 12use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot};
13use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; 13use ra_vfs_glob::{Glob, RustPackageFilterBuilder};
14use relative_path::RelativePathBuf; 14use relative_path::RelativePathBuf;
15 15
@@ -210,6 +210,10 @@ impl WorldSnapshot {
210 Ok(url) 210 Ok(url)
211 } 211 }
212 212
213 pub fn file_line_endings(&self, id: FileId) -> LineEndings {
214 self.vfs.read().file_line_endings(VfsFile(id.0))
215 }
216
213 pub fn path_to_uri(&self, root: SourceRootId, path: &RelativePathBuf) -> Result<Url> { 217 pub fn path_to_uri(&self, root: SourceRootId, path: &RelativePathBuf) -> Result<Url> {
214 let base = self.vfs.read().root2path(VfsRoot(root.0)); 218 let base = self.vfs.read().root2path(VfsRoot(root.0));
215 let path = path.to_path(base); 219 let path = path.to_path(base);
diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs
index de3bd5bc5..152681062 100644
--- a/crates/ra_lsp_server/tests/heavy_tests/main.rs
+++ b/crates/ra_lsp_server/tests/heavy_tests/main.rs
@@ -208,7 +208,7 @@ pub use std::collections::HashMap;
208 "range": { 208 "range": {
209 "end": { 209 "end": {
210 "character": 0, 210 "character": 0,
211 "line": 6 211 "line": 7
212 }, 212 },
213 "start": { 213 "start": {
214 "character": 0, 214 "character": 0,
@@ -414,3 +414,49 @@ fn main() {{}}
414 let elapsed = start.elapsed(); 414 let elapsed = start.elapsed();
415 assert!(elapsed.as_millis() < 2000, "typing enter took {:?}", elapsed); 415 assert!(elapsed.as_millis() < 2000, "typing enter took {:?}", elapsed);
416} 416}
417
418#[test]
419fn preserves_dos_line_endings() {
420 let server = Project::with_fixture(
421 &"
422//- Cargo.toml
423[package]
424name = \"foo\"
425version = \"0.0.0\"
426
427//- src/main.rs
428/// Some Docs\r\nfn main() {}
429",
430 )
431 .server();
432
433 server.request::<OnEnter>(
434 TextDocumentPositionParams {
435 text_document: server.doc_id("src/main.rs"),
436 position: Position { line: 0, character: 8 },
437 },
438 json!({
439 "cursorPosition": {
440 "position": { "line": 1, "character": 4 },
441 "textDocument": { "uri": "file:///[..]src/main.rs" }
442 },
443 "label": "on enter",
444 "workspaceEdit": {
445 "documentChanges": [
446 {
447 "edits": [
448 {
449 "newText": "\r\n/// ",
450 "range": {
451 "end": { "line": 0, "character": 8 },
452 "start": { "line": 0, "character": 8 }
453 }
454 }
455 ],
456 "textDocument": { "uri": "file:///[..]src/main.rs", "version": null }
457 }
458 ]
459 }
460 }),
461 );
462}
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml
index 5f8585878..0ead277b2 100644
--- a/crates/ra_syntax/Cargo.toml
+++ b/crates/ra_syntax/Cargo.toml
@@ -10,7 +10,7 @@ repository = "https://github.com/rust-analyzer/rust-analyzer"
10[dependencies] 10[dependencies]
11itertools = "0.8.0" 11itertools = "0.8.0"
12rowan = "0.6.1" 12rowan = "0.6.1"
13ra_rustc_lexer = { version = "0.1.0-pre.2" } 13ra_rustc_lexer = { version = "0.1.0-pre.3", features = ["unicode-xid"] }
14 14
15# ideally, `serde` should be enabled by `ra_lsp_server`, but we enable it here 15# ideally, `serde` should be enabled by `ra_lsp_server`, but we enable it here
16# to reduce number of compilations 16# to reduce number of compilations
diff --git a/crates/ra_syntax/src/parsing/lexer.rs b/crates/ra_syntax/src/parsing/lexer.rs
index 06822ea91..bdb01d40b 100644
--- a/crates/ra_syntax/src/parsing/lexer.rs
+++ b/crates/ra_syntax/src/parsing/lexer.rs
@@ -12,16 +12,16 @@ pub struct Token {
12 pub len: TextUnit, 12 pub len: TextUnit,
13} 13}
14 14
15fn match_literal_kind(kind: ra_rustc_lexer::LiteralKind) -> SyntaxKind { 15fn match_literal_kind(kind: rustc_lexer::LiteralKind) -> SyntaxKind {
16 match kind { 16 match kind {
17 ra_rustc_lexer::LiteralKind::Int { .. } => INT_NUMBER, 17 rustc_lexer::LiteralKind::Int { .. } => INT_NUMBER,
18 ra_rustc_lexer::LiteralKind::Float { .. } => FLOAT_NUMBER, 18 rustc_lexer::LiteralKind::Float { .. } => FLOAT_NUMBER,
19 ra_rustc_lexer::LiteralKind::Char { .. } => CHAR, 19 rustc_lexer::LiteralKind::Char { .. } => CHAR,
20 ra_rustc_lexer::LiteralKind::Byte { .. } => BYTE, 20 rustc_lexer::LiteralKind::Byte { .. } => BYTE,
21 ra_rustc_lexer::LiteralKind::Str { .. } => STRING, 21 rustc_lexer::LiteralKind::Str { .. } => STRING,
22 ra_rustc_lexer::LiteralKind::ByteStr { .. } => BYTE_STRING, 22 rustc_lexer::LiteralKind::ByteStr { .. } => BYTE_STRING,
23 ra_rustc_lexer::LiteralKind::RawStr { .. } => RAW_STRING, 23 rustc_lexer::LiteralKind::RawStr { .. } => RAW_STRING,
24 ra_rustc_lexer::LiteralKind::RawByteStr { .. } => RAW_BYTE_STRING, 24 rustc_lexer::LiteralKind::RawByteStr { .. } => RAW_BYTE_STRING,
25 } 25 }
26} 26}
27 27
@@ -32,32 +32,17 @@ pub fn tokenize(text: &str) -> Vec<Token> {
32 } 32 }
33 let mut text = text; 33 let mut text = text;
34 let mut acc = Vec::new(); 34 let mut acc = Vec::new();
35 if let Some(len) = ra_rustc_lexer::strip_shebang(text) { 35 if let Some(len) = rustc_lexer::strip_shebang(text) {
36 acc.push(Token { kind: SHEBANG, len: TextUnit::from_usize(len) }); 36 acc.push(Token { kind: SHEBANG, len: TextUnit::from_usize(len) });
37 text = &text[len..]; 37 text = &text[len..];
38 } 38 }
39 while !text.is_empty() { 39 while !text.is_empty() {
40 let rustc_token = ra_rustc_lexer::first_token(text); 40 let rustc_token = rustc_lexer::first_token(text);
41 macro_rules! decompose {
42 ($t1:expr, $t2:expr) => {{
43 acc.push(Token { kind: $t1, len: 1.into() });
44 acc.push(Token { kind: $t2, len: 1.into() });
45 text = &text[2..];
46 continue;
47 }};
48 ($t1:expr, $t2:expr, $t3:expr) => {{
49 acc.push(Token { kind: $t1, len: 1.into() });
50 acc.push(Token { kind: $t2, len: 1.into() });
51 acc.push(Token { kind: $t3, len: 1.into() });
52 text = &text[3..];
53 continue;
54 }};
55 }
56 let kind = match rustc_token.kind { 41 let kind = match rustc_token.kind {
57 ra_rustc_lexer::TokenKind::LineComment => COMMENT, 42 rustc_lexer::TokenKind::LineComment => COMMENT,
58 ra_rustc_lexer::TokenKind::BlockComment { .. } => COMMENT, 43 rustc_lexer::TokenKind::BlockComment { .. } => COMMENT,
59 ra_rustc_lexer::TokenKind::Whitespace => WHITESPACE, 44 rustc_lexer::TokenKind::Whitespace => WHITESPACE,
60 ra_rustc_lexer::TokenKind::Ident => { 45 rustc_lexer::TokenKind::Ident => {
61 let token_text = &text[..rustc_token.len]; 46 let token_text = &text[..rustc_token.len];
62 if token_text == "_" { 47 if token_text == "_" {
63 UNDERSCORE 48 UNDERSCORE
@@ -65,62 +50,37 @@ pub fn tokenize(text: &str) -> Vec<Token> {
65 SyntaxKind::from_keyword(&text[..rustc_token.len]).unwrap_or(IDENT) 50 SyntaxKind::from_keyword(&text[..rustc_token.len]).unwrap_or(IDENT)
66 } 51 }
67 } 52 }
68 ra_rustc_lexer::TokenKind::RawIdent => IDENT, 53 rustc_lexer::TokenKind::RawIdent => IDENT,
69 ra_rustc_lexer::TokenKind::Literal { kind, .. } => match_literal_kind(kind), 54 rustc_lexer::TokenKind::Literal { kind, .. } => match_literal_kind(kind),
70 ra_rustc_lexer::TokenKind::Lifetime { .. } => LIFETIME, 55 rustc_lexer::TokenKind::Lifetime { .. } => LIFETIME,
71 ra_rustc_lexer::TokenKind::Semi => SEMI, 56 rustc_lexer::TokenKind::Semi => SEMI,
72 ra_rustc_lexer::TokenKind::Comma => COMMA, 57 rustc_lexer::TokenKind::Comma => COMMA,
73 ra_rustc_lexer::TokenKind::DotDotDot => decompose!(DOT, DOT, DOT), 58 rustc_lexer::TokenKind::Dot => DOT,
74 ra_rustc_lexer::TokenKind::DotDotEq => decompose!(DOT, DOT, EQ), 59 rustc_lexer::TokenKind::OpenParen => L_PAREN,
75 ra_rustc_lexer::TokenKind::DotDot => decompose!(DOT, DOT), 60 rustc_lexer::TokenKind::CloseParen => R_PAREN,
76 ra_rustc_lexer::TokenKind::Dot => DOT, 61 rustc_lexer::TokenKind::OpenBrace => L_CURLY,
77 ra_rustc_lexer::TokenKind::OpenParen => L_PAREN, 62 rustc_lexer::TokenKind::CloseBrace => R_CURLY,
78 ra_rustc_lexer::TokenKind::CloseParen => R_PAREN, 63 rustc_lexer::TokenKind::OpenBracket => L_BRACK,
79 ra_rustc_lexer::TokenKind::OpenBrace => L_CURLY, 64 rustc_lexer::TokenKind::CloseBracket => R_BRACK,
80 ra_rustc_lexer::TokenKind::CloseBrace => R_CURLY, 65 rustc_lexer::TokenKind::At => AT,
81 ra_rustc_lexer::TokenKind::OpenBracket => L_BRACK, 66 rustc_lexer::TokenKind::Pound => POUND,
82 ra_rustc_lexer::TokenKind::CloseBracket => R_BRACK, 67 rustc_lexer::TokenKind::Tilde => TILDE,
83 ra_rustc_lexer::TokenKind::At => AT, 68 rustc_lexer::TokenKind::Question => QUESTION,
84 ra_rustc_lexer::TokenKind::Pound => POUND, 69 rustc_lexer::TokenKind::Colon => COLON,
85 ra_rustc_lexer::TokenKind::Tilde => TILDE, 70 rustc_lexer::TokenKind::Dollar => DOLLAR,
86 ra_rustc_lexer::TokenKind::Question => QUESTION, 71 rustc_lexer::TokenKind::Eq => EQ,
87 ra_rustc_lexer::TokenKind::ColonColon => decompose!(COLON, COLON), 72 rustc_lexer::TokenKind::Not => EXCL,
88 ra_rustc_lexer::TokenKind::Colon => COLON, 73 rustc_lexer::TokenKind::Lt => L_ANGLE,
89 ra_rustc_lexer::TokenKind::Dollar => DOLLAR, 74 rustc_lexer::TokenKind::Gt => R_ANGLE,
90 ra_rustc_lexer::TokenKind::EqEq => decompose!(EQ, EQ), 75 rustc_lexer::TokenKind::Minus => MINUS,
91 ra_rustc_lexer::TokenKind::Eq => EQ, 76 rustc_lexer::TokenKind::And => AMP,
92 ra_rustc_lexer::TokenKind::FatArrow => decompose!(EQ, R_ANGLE), 77 rustc_lexer::TokenKind::Or => PIPE,
93 ra_rustc_lexer::TokenKind::Ne => decompose!(EXCL, EQ), 78 rustc_lexer::TokenKind::Plus => PLUS,
94 ra_rustc_lexer::TokenKind::Not => EXCL, 79 rustc_lexer::TokenKind::Star => STAR,
95 ra_rustc_lexer::TokenKind::Le => decompose!(L_ANGLE, EQ), 80 rustc_lexer::TokenKind::Slash => SLASH,
96 ra_rustc_lexer::TokenKind::LArrow => decompose!(COLON, MINUS), 81 rustc_lexer::TokenKind::Caret => CARET,
97 ra_rustc_lexer::TokenKind::Lt => L_ANGLE, 82 rustc_lexer::TokenKind::Percent => PERCENT,
98 ra_rustc_lexer::TokenKind::ShlEq => decompose!(L_ANGLE, L_ANGLE, EQ), 83 rustc_lexer::TokenKind::Unknown => ERROR,
99 ra_rustc_lexer::TokenKind::Shl => decompose!(L_ANGLE, L_ANGLE),
100 ra_rustc_lexer::TokenKind::Ge => decompose!(R_ANGLE, EQ),
101 ra_rustc_lexer::TokenKind::Gt => R_ANGLE,
102 ra_rustc_lexer::TokenKind::ShrEq => decompose!(R_ANGLE, R_ANGLE, EQ),
103 ra_rustc_lexer::TokenKind::Shr => decompose!(R_ANGLE, R_ANGLE),
104 ra_rustc_lexer::TokenKind::RArrow => decompose!(MINUS, R_ANGLE),
105 ra_rustc_lexer::TokenKind::Minus => MINUS,
106 ra_rustc_lexer::TokenKind::MinusEq => decompose!(MINUS, EQ),
107 ra_rustc_lexer::TokenKind::And => AMP,
108 ra_rustc_lexer::TokenKind::AndAnd => decompose!(AMP, AMP),
109 ra_rustc_lexer::TokenKind::AndEq => decompose!(AMP, EQ),
110 ra_rustc_lexer::TokenKind::Or => PIPE,
111 ra_rustc_lexer::TokenKind::OrOr => decompose!(PIPE, PIPE),
112 ra_rustc_lexer::TokenKind::OrEq => decompose!(PIPE, EQ),
113 ra_rustc_lexer::TokenKind::PlusEq => decompose!(PLUS, EQ),
114 ra_rustc_lexer::TokenKind::Plus => PLUS,
115 ra_rustc_lexer::TokenKind::StarEq => decompose!(STAR, EQ),
116 ra_rustc_lexer::TokenKind::Star => STAR,
117 ra_rustc_lexer::TokenKind::SlashEq => decompose!(SLASH, EQ),
118 ra_rustc_lexer::TokenKind::Slash => SLASH,
119 ra_rustc_lexer::TokenKind::CaretEq => decompose!(CARET, EQ),
120 ra_rustc_lexer::TokenKind::Caret => CARET,
121 ra_rustc_lexer::TokenKind::PercentEq => decompose!(PERCENT, EQ),
122 ra_rustc_lexer::TokenKind::Percent => PERCENT,
123 ra_rustc_lexer::TokenKind::Unknown => ERROR,
124 }; 84 };
125 let token = Token { kind, len: TextUnit::from_usize(rustc_token.len) }; 85 let token = Token { kind, len: TextUnit::from_usize(rustc_token.len) };
126 acc.push(token); 86 acc.push(token);
@@ -130,12 +90,12 @@ pub fn tokenize(text: &str) -> Vec<Token> {
130} 90}
131 91
132pub fn classify_literal(text: &str) -> Option<Token> { 92pub fn classify_literal(text: &str) -> Option<Token> {
133 let t = ra_rustc_lexer::first_token(text); 93 let t = rustc_lexer::first_token(text);
134 if t.len != text.len() { 94 if t.len != text.len() {
135 return None; 95 return None;
136 } 96 }
137 let kind = match t.kind { 97 let kind = match t.kind {
138 ra_rustc_lexer::TokenKind::Literal { kind, .. } => match_literal_kind(kind), 98 rustc_lexer::TokenKind::Literal { kind, .. } => match_literal_kind(kind),
139 _ => return None, 99 _ => return None,
140 }; 100 };
141 Some(Token { kind, len: TextUnit::from_usize(t.len) }) 101 Some(Token { kind, len: TextUnit::from_usize(t.len) })
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs
index 2bb3c0a03..a8c789e0c 100644
--- a/crates/ra_syntax/src/validation.rs
+++ b/crates/ra_syntax/src/validation.rs
@@ -1,6 +1,6 @@
1mod block; 1mod block;
2 2
3use ra_rustc_lexer::unescape; 3use rustc_lexer::unescape;
4 4
5use crate::{ 5use crate::{
6 algo::visit::{visitor_ctx, VisitorCtx}, 6 algo::visit::{visitor_ctx, VisitorCtx},
@@ -32,64 +32,62 @@ pub enum EscapeError {
32 NonAsciiCharInByte, 32 NonAsciiCharInByte,
33} 33}
34 34
35impl From<ra_rustc_lexer::unescape::EscapeError> for EscapeError { 35impl From<rustc_lexer::unescape::EscapeError> for EscapeError {
36 fn from(err: ra_rustc_lexer::unescape::EscapeError) -> Self { 36 fn from(err: rustc_lexer::unescape::EscapeError) -> Self {
37 match err { 37 match err {
38 ra_rustc_lexer::unescape::EscapeError::ZeroChars => EscapeError::ZeroChars, 38 rustc_lexer::unescape::EscapeError::ZeroChars => EscapeError::ZeroChars,
39 ra_rustc_lexer::unescape::EscapeError::MoreThanOneChar => EscapeError::MoreThanOneChar, 39 rustc_lexer::unescape::EscapeError::MoreThanOneChar => EscapeError::MoreThanOneChar,
40 ra_rustc_lexer::unescape::EscapeError::LoneSlash => EscapeError::LoneSlash, 40 rustc_lexer::unescape::EscapeError::LoneSlash => EscapeError::LoneSlash,
41 ra_rustc_lexer::unescape::EscapeError::InvalidEscape => EscapeError::InvalidEscape, 41 rustc_lexer::unescape::EscapeError::InvalidEscape => EscapeError::InvalidEscape,
42 ra_rustc_lexer::unescape::EscapeError::BareCarriageReturn 42 rustc_lexer::unescape::EscapeError::BareCarriageReturn
43 | ra_rustc_lexer::unescape::EscapeError::BareCarriageReturnInRawString => { 43 | rustc_lexer::unescape::EscapeError::BareCarriageReturnInRawString => {
44 EscapeError::BareCarriageReturn 44 EscapeError::BareCarriageReturn
45 } 45 }
46 ra_rustc_lexer::unescape::EscapeError::EscapeOnlyChar => EscapeError::EscapeOnlyChar, 46 rustc_lexer::unescape::EscapeError::EscapeOnlyChar => EscapeError::EscapeOnlyChar,
47 ra_rustc_lexer::unescape::EscapeError::TooShortHexEscape => { 47 rustc_lexer::unescape::EscapeError::TooShortHexEscape => EscapeError::TooShortHexEscape,
48 EscapeError::TooShortHexEscape 48 rustc_lexer::unescape::EscapeError::InvalidCharInHexEscape => {
49 }
50 ra_rustc_lexer::unescape::EscapeError::InvalidCharInHexEscape => {
51 EscapeError::InvalidCharInHexEscape 49 EscapeError::InvalidCharInHexEscape
52 } 50 }
53 ra_rustc_lexer::unescape::EscapeError::OutOfRangeHexEscape => { 51 rustc_lexer::unescape::EscapeError::OutOfRangeHexEscape => {
54 EscapeError::OutOfRangeHexEscape 52 EscapeError::OutOfRangeHexEscape
55 } 53 }
56 ra_rustc_lexer::unescape::EscapeError::NoBraceInUnicodeEscape => { 54 rustc_lexer::unescape::EscapeError::NoBraceInUnicodeEscape => {
57 EscapeError::NoBraceInUnicodeEscape 55 EscapeError::NoBraceInUnicodeEscape
58 } 56 }
59 ra_rustc_lexer::unescape::EscapeError::InvalidCharInUnicodeEscape => { 57 rustc_lexer::unescape::EscapeError::InvalidCharInUnicodeEscape => {
60 EscapeError::InvalidCharInUnicodeEscape 58 EscapeError::InvalidCharInUnicodeEscape
61 } 59 }
62 ra_rustc_lexer::unescape::EscapeError::EmptyUnicodeEscape => { 60 rustc_lexer::unescape::EscapeError::EmptyUnicodeEscape => {
63 EscapeError::EmptyUnicodeEscape 61 EscapeError::EmptyUnicodeEscape
64 } 62 }
65 ra_rustc_lexer::unescape::EscapeError::UnclosedUnicodeEscape => { 63 rustc_lexer::unescape::EscapeError::UnclosedUnicodeEscape => {
66 EscapeError::UnclosedUnicodeEscape 64 EscapeError::UnclosedUnicodeEscape
67 } 65 }
68 ra_rustc_lexer::unescape::EscapeError::LeadingUnderscoreUnicodeEscape => { 66 rustc_lexer::unescape::EscapeError::LeadingUnderscoreUnicodeEscape => {
69 EscapeError::LeadingUnderscoreUnicodeEscape 67 EscapeError::LeadingUnderscoreUnicodeEscape
70 } 68 }
71 ra_rustc_lexer::unescape::EscapeError::OverlongUnicodeEscape => { 69 rustc_lexer::unescape::EscapeError::OverlongUnicodeEscape => {
72 EscapeError::OverlongUnicodeEscape 70 EscapeError::OverlongUnicodeEscape
73 } 71 }
74 ra_rustc_lexer::unescape::EscapeError::LoneSurrogateUnicodeEscape => { 72 rustc_lexer::unescape::EscapeError::LoneSurrogateUnicodeEscape => {
75 EscapeError::LoneSurrogateUnicodeEscape 73 EscapeError::LoneSurrogateUnicodeEscape
76 } 74 }
77 ra_rustc_lexer::unescape::EscapeError::OutOfRangeUnicodeEscape => { 75 rustc_lexer::unescape::EscapeError::OutOfRangeUnicodeEscape => {
78 EscapeError::OutOfRangeUnicodeEscape 76 EscapeError::OutOfRangeUnicodeEscape
79 } 77 }
80 ra_rustc_lexer::unescape::EscapeError::UnicodeEscapeInByte => { 78 rustc_lexer::unescape::EscapeError::UnicodeEscapeInByte => {
81 EscapeError::UnicodeEscapeInByte 79 EscapeError::UnicodeEscapeInByte
82 } 80 }
83 ra_rustc_lexer::unescape::EscapeError::NonAsciiCharInByte 81 rustc_lexer::unescape::EscapeError::NonAsciiCharInByte
84 | ra_rustc_lexer::unescape::EscapeError::NonAsciiCharInByteString => { 82 | rustc_lexer::unescape::EscapeError::NonAsciiCharInByteString => {
85 EscapeError::NonAsciiCharInByte 83 EscapeError::NonAsciiCharInByte
86 } 84 }
87 } 85 }
88 } 86 }
89} 87}
90 88
91impl From<ra_rustc_lexer::unescape::EscapeError> for SyntaxErrorKind { 89impl From<rustc_lexer::unescape::EscapeError> for SyntaxErrorKind {
92 fn from(err: ra_rustc_lexer::unescape::EscapeError) -> Self { 90 fn from(err: rustc_lexer::unescape::EscapeError) -> Self {
93 SyntaxErrorKind::EscapeError(err.into()) 91 SyntaxErrorKind::EscapeError(err.into())
94 } 92 }
95} 93}
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index ea99ac062..816d01f09 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -134,21 +134,25 @@ pub fn parse_fixture(fixture: &str) -> Vec<FixtureEntry> {
134 } 134 }
135 }; 135 };
136 }; 136 };
137
137 let margin = fixture 138 let margin = fixture
138 .lines() 139 .lines()
139 .filter(|it| it.trim_start().starts_with("//-")) 140 .filter(|it| it.trim_start().starts_with("//-"))
140 .map(|it| it.len() - it.trim_start().len()) 141 .map(|it| it.len() - it.trim_start().len())
141 .next() 142 .next()
142 .expect("empty fixture"); 143 .expect("empty fixture");
143 let lines = fixture.lines().filter_map(|line| { 144
144 if line.len() >= margin { 145 let lines = fixture
145 assert!(line[..margin].trim().is_empty()); 146 .split('\n') // don't use `.lines` to not drop `\r\n`
146 Some(&line[margin..]) 147 .filter_map(|line| {
147 } else { 148 if line.len() >= margin {
148 assert!(line.trim().is_empty()); 149 assert!(line[..margin].trim().is_empty());
149 None 150 Some(&line[margin..])
150 } 151 } else {
151 }); 152 assert!(line.trim().is_empty());
153 None
154 }
155 });
152 156
153 for line in lines { 157 for line in lines {
154 if line.starts_with("//-") { 158 if line.starts_with("//-") {