From 42046fc27cf321dc7c0fc70bdf11e8e4a024b32a Mon Sep 17 00:00:00 2001 From: kngwyu Date: Sun, 17 Jun 2018 22:59:07 +0900 Subject: [PATCH] Replace type Point and SourceByteRange with BytePoint and ByteRange --- Cargo.lock | 57 +++++++ Cargo.toml | 2 + src/racer/ast.rs | 98 +++++------ src/racer/codecleaner.rs | 2 +- src/racer/codeiter.rs | 70 ++++---- src/racer/core.rs | 224 +++++++++++++++++-------- src/racer/lib.rs | 10 +- src/racer/matchers.rs | 168 +++++++++---------- src/racer/nameres.rs | 349 ++++++++++++++++++++++++--------------- src/racer/scopes.rs | 189 +++++++++++---------- src/racer/typeinf.rs | 137 ++++++++------- src/racer/util.rs | 27 +-- 12 files changed, 797 insertions(+), 536 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a4f5bb9d..592c13e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -262,6 +262,16 @@ dependencies = [ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "derive_more" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "dtoa" version = "0.4.2" @@ -669,6 +679,14 @@ name = "pkg-config" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "proc-macro2" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "proc-macro2" version = "0.4.3" @@ -687,6 +705,14 @@ name = "quote" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "quote" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "quote" version = "0.6.2" @@ -701,11 +727,13 @@ version = "2.0.14" dependencies = [ "cargo 0.29.0 (git+https://github.com/rust-lang/cargo?rev=d775dddd31ce56a78e2f368f546311bfd94135b6)", "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "racer-testutils 0.1.0", + "rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-ap-syntax 151.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -768,6 +796,11 @@ dependencies = [ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rls-span" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "rustc-ap-arena" version = "151.0.0" @@ -892,6 +925,14 @@ dependencies = [ "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rustc_version" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "same-file" version = "1.0.2" @@ -1006,6 +1047,16 @@ dependencies = [ "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "syn" +version = "0.13.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "0.14.0" @@ -1241,6 +1292,7 @@ dependencies = [ "checksum crypto-hash 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "09de9ee0fc255ace04c7fa0763c9395a945c37c8292bb554f8d48361d1dcf1b4" "checksum curl 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "aaf20bbe084f285f215eef2165feed70d6b75ba29cad24469badb853a4a287d0" "checksum curl-sys 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "71c63a540a9ee4e15e56c3ed9b11a2f121239b9f6d7b7fe30f616e048148df9a" +"checksum derive_more 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46c7f14685a20f5dd08e7f754f2ea8cc064d8f4214ae21116c106a2768ba7b9b" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" "checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0" "checksum ena 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "88dc8393b3c7352f94092497f6b52019643e493b6b890eb417cdb7c46117e621" @@ -1290,9 +1342,11 @@ dependencies = [ "checksum parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "110d5ee3593dbb73f56294327fe5668bcc997897097cbc76b51e7aed3f52452f" +"checksum proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1b06e2f335f48d24442b35a19df506a835fb3547bc3c06ef27340da9acf5cae7" "checksum proc-macro2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a45f2f0ae0b5757f6fe9e68745ba25f5246aea3598984ed81d013865873c1f84" "checksum quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eda5fe9b71976e62bc81b781206aaa076401769b2143379d3eb2118388babac4" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" +"checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8" "checksum quote 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9e53eeda07ddbd8b057dde66d9beded11d0dfda13f0db0769e6b71d6bcf2074e" "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" "checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" @@ -1300,6 +1354,7 @@ dependencies = [ "checksum regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "aec3f58d903a7d2a9dc2bf0e41a746f4530e0cab6b615494e058f67a3ef947fb" "checksum regex-syntax 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b2550876c31dc914696a6c2e01cbce8afba79a93c8ae979d2fe051c0230b3756" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" +"checksum rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d7c7046dc6a92f2ae02ed302746db4382e75131b9ce20ce967259f6b5867a6a" "checksum rustc-ap-arena 151.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "746ee5f3cf21c39b18ffd086a89d5b1c3b1e955c3e4bbd2b079b36c48ad68d46" "checksum rustc-ap-rustc_cratesio_shim 151.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "827fb629eb8e598bac61759eed857400d6ac9c9db448a4e9adb13d5d59509eb3" "checksum rustc-ap-rustc_data_structures 151.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d8890aef70f20c039b04238f5e43c087df94020948c5ef860687ce07468a762b" @@ -1312,6 +1367,7 @@ dependencies = [ "checksum rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7540fc8b0c49f096ee9c961cda096467dce8084bec6bdca2fc83895fd9b28cb8" "checksum rustc-rayon 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1aa5cd8c3a706edb19b6ec6aa7b056bdc635b6e99c5cf7014f9af9d92f15e99" "checksum rustc-rayon-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d69983f8613a9c3ba1a3bbf5e8bdf2fd5c42317b1d8dd8623ca8030173bf8a6b" +"checksum rustc_version 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a54aa04a10c68c1c4eacb4337fd883b435997ede17a9385784b990777686b09a" "checksum same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfb6eded0b06a0b512c8ddbcf04089138c9b4362c2f696f3c3d76039d68f3637" "checksum schannel 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "85fd9df495640643ad2d00443b3d78aae69802ad488debab4f1dd52fc1806ade" "checksum scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28" @@ -1328,6 +1384,7 @@ dependencies = [ "checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" +"checksum syn 0.13.11 (registry+https://github.com/rust-lang/crates.io-index)" = "14f9bf6292f3a61d2c716723fdb789a41bbe104168e6f496dc6497e531ea1b9b" "checksum syn 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "99d991a9e7c33123925e511baab68f7ec25c3795962fe326a2395e5a42a614f0" "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd" diff --git a/Cargo.toml b/Cargo.toml index 663a7956..2f520a5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,8 @@ env_logger = "0.5" clap = "2.19" lazy_static = "1.0" humantime = "1.1" +derive_more = "0.11.0" +rls-span = "0.4.0" [dev-dependencies] racer-testutils = { path = "testutils" } diff --git a/src/racer/ast.rs b/src/racer/ast.rs index 8294b738..12406b6d 100644 --- a/src/racer/ast.rs +++ b/src/racer/ast.rs @@ -1,4 +1,4 @@ -use core::{self, Match, MatchType, Scope, Ty, Session, SessionExt, Point, SourceByteRange}; +use core::{self, Match, MatchType, Scope, Ty, Session, SessionExt, BytePos, ByteRange}; use typeinf; use nameres::{self, resolve_path_with_str}; use scopes; @@ -57,7 +57,7 @@ where }).is_some() } -fn destruct_span(span: Span) -> (u32, u32) { +pub(crate) fn destruct_span(span: Span) -> (u32, u32) { let codemap::BytePos(lo) = span.lo(); let codemap::BytePos(hi) = span.hi(); (lo, hi) @@ -162,7 +162,7 @@ impl<'ast> visit::Visitor<'ast> for UseVisitor { } pub struct PatBindVisitor { - ident_points: Vec + ident_points: Vec } impl<'ast> visit::Visitor<'ast> for PatBindVisitor { @@ -185,8 +185,7 @@ impl<'ast> visit::Visitor<'ast> for PatBindVisitor { fn visit_pat(&mut self, p: &ast::Pat) { match p.node { PatKind::Ident(_ , ref spannedident, _) => { - let (lo, hi) = destruct_span(spannedident.span); - self.ident_points.push((lo as usize, hi as usize)); + self.ident_points.push(spannedident.span.into()); } _ => { visit::walk_pat(self, p); @@ -196,15 +195,14 @@ impl<'ast> visit::Visitor<'ast> for PatBindVisitor { } pub struct PatVisitor { - ident_points: Vec + ident_points: Vec } impl<'ast> visit::Visitor<'ast> for PatVisitor { fn visit_pat(&mut self, p: &ast::Pat) { match p.node { PatKind::Ident(_ , ref spannedident, _) => { - let (lo, hi) = destruct_span(spannedident.span); - self.ident_points.push((lo as usize, hi as usize)); + self.ident_points.push(spannedident.span.into()); } _ => { visit::walk_pat(self, p); } } @@ -247,25 +245,26 @@ fn to_racer_ty(ty: &ast::Ty, scope: &Scope) -> Option { } } -fn point_is_in_span(point: u32, span: &Span) -> bool { +fn point_is_in_span(point: BytePos, span: &Span) -> bool { + let point: u32 = point.0 as u32; let (lo, hi) = destruct_span(*span); point >= lo && point < hi } // The point must point to an ident within the pattern. fn destructure_pattern_to_ty(pat: &ast::Pat, - point: Point, + point: BytePos, ty: &Ty, scope: &Scope, session: &Session) -> Option { - debug!("destructure_pattern_to_ty point {} ty {:?} |||||||| pat: {:?}", point, ty, pat); + debug!("destructure_pattern_to_ty point {:?} ty {:?} |||||||| pat: {:?}", point, ty, pat); match pat.node { PatKind::Ident(_ , ref spannedident, _) => { - if point_is_in_span(point as u32, &spannedident.span) { + if point_is_in_span(point, &spannedident.span) { debug!("destructure_pattern_to_ty matched an ident!"); Some(ty.clone()) } else { - panic!("Expecting the point to be in the patident span. pt: {}", point); + panic!("Expecting the point to be in the patident span. pt: {:?}", point); } } PatKind::Tuple(ref tuple_elements, _) => { @@ -273,7 +272,7 @@ fn destructure_pattern_to_ty(pat: &ast::Pat, Ty::Tuple(ref typeelems) => { let mut res = None; for (i, p) in tuple_elements.iter().enumerate() { - if point_is_in_span(point as u32, &p.span) { + if point_is_in_span(point, &p.span) { let ty = &typeelems[i]; res = destructure_pattern_to_ty(p, point, ty, scope, session); break; @@ -292,7 +291,7 @@ fn destructure_pattern_to_ty(pat: &ast::Pat, let mut res = None; for (i, p) in children.iter().enumerate() { - if point_is_in_span(point as u32, &p.span) { + if point_is_in_span(point, &p.span) { res = typeinf::get_tuplestruct_field_type(i, &m, session) .and_then(|ty| @@ -319,7 +318,7 @@ fn destructure_pattern_to_ty(pat: &ast::Pat, let mut res = None; for child in children { - if point_is_in_span(point as u32, &child.span) { + if point_is_in_span(point, &child.span) { res = typeinf::get_struct_field_type(&child.node.ident.name.as_str(), &m, session) .and_then(|ty| if let Some(Ty::Match(ref contextmatch)) = contextty { @@ -349,7 +348,7 @@ struct LetTypeVisitor<'c: 's, 's> { scope: Scope, session: &'s Session<'c>, srctxt: String, - pos: Point, // pos is relative to the srctxt, scope is global + pos: BytePos, // pos is relative to the srctxt, scope is global result: Option } @@ -402,7 +401,7 @@ impl<'c, 's, 'ast> visit::Visitor<'ast> for LetTypeVisitor<'c, 's> { }); } - debug!("LetTypeVisitor: ty is {:?}. pos is {}, src is |{}|", ty, self.pos, self.srctxt); + debug!("LetTypeVisitor: ty is {:?}. pos is {:?}, src is |{}|", ty, self.pos, self.srctxt); self.result = ty.and_then(|ty| destructure_pattern_to_ty(&local.pat, self.pos, &ty, &self.scope, self.session)) .and_then(|ty| path_to_match(ty, self.session)); @@ -412,7 +411,7 @@ impl<'c, 's, 'ast> visit::Visitor<'ast> for LetTypeVisitor<'c, 's> { struct MatchTypeVisitor<'c: 's, 's> { scope: Scope, session: &'s Session<'c>, - pos: Point, // pos is relative to the srctxt, scope is global + pos: BytePos, // pos is relative to the srctxt, scope is global result: Option } @@ -429,7 +428,7 @@ impl<'c, 's, 'ast> visit::Visitor<'ast> for MatchTypeVisitor<'c, 's> { for arm in arms { for pattern in &arm.pats { - if point_is_in_span(self.pos as u32, &pattern.span) { + if point_is_in_span(self.pos, &pattern.span) { debug!("PHIL point is in pattern |{:?}|", pattern); self.result = v.result.as_ref().and_then(|ty| destructure_pattern_to_ty(pattern, self.pos, ty, &self.scope, self.session)) @@ -441,7 +440,12 @@ impl<'c, 's, 'ast> visit::Visitor<'ast> for MatchTypeVisitor<'c, 's> { } } -fn resolve_ast_path(path: &ast::Path, filepath: &Path, pos: Point, session: &Session) -> Option { +fn resolve_ast_path( + path: &ast::Path, + filepath: &Path, + pos: BytePos, + session: &Session +) -> Option { debug!("resolve_ast_path {:?}", to_racer_path(path)); nameres::resolve_path_with_str(&to_racer_path(path), filepath, pos, core::SearchType::ExactMatch, core::Namespace::Both, session).nth(0) @@ -492,7 +496,7 @@ fn path_to_match(ty: Ty, session: &Session) -> Option { } } -fn find_type_match(path: &core::Path, fpath: &Path, pos: Point, session: &Session) -> Option { +fn find_type_match(path: &core::Path, fpath: &Path, pos: BytePos, session: &Session) -> Option { debug!("find_type_match {:?}, {:?}", path, fpath); let res = resolve_path_with_str(path, fpath, pos, core::SearchType::ExactMatch, core::Namespace::Type, session).nth(0).and_then(|m| { @@ -519,11 +523,11 @@ fn find_type_match(path: &core::Path, fpath: &Path, pos: Point, session: &Sessio fn get_type_of_typedef(m: &Match, session: &Session, fpath: &Path) -> Option { debug!("get_type_of_typedef match is {:?}", m); let msrc = session.load_file_and_mask_comments(&m.filepath); - let blobstart = m.point - 5; // - 5 because 'type ' + let blobstart = m.point - 5.into(); // - 5 because 'type ' let blob = msrc.from(blobstart); - blob.iter_stmts().nth(0).and_then(|(start, end)| { - let blob = msrc[blobstart + start..blobstart+end].to_owned(); + blob.iter_stmts().nth(0).and_then(|range| { + let blob = msrc[range.shift(blobstart).to_range()].to_owned(); debug!("get_type_of_typedef blob string {}", blob); let res = parse_type(blob); debug!("get_type_of_typedef parsed type {:?}", res.type_); @@ -533,8 +537,8 @@ fn get_type_of_typedef(m: &Match, session: &Session, fpath: &Path) -> Option visit::Visitor<'ast> for ExprTypeVisitor<'c, 's> { let codemap::BytePos(lo) = path.span.lo(); self.result = resolve_ast_path(path, &self.scope.filepath, - self.scope.point + lo as usize, + self.scope.point + lo.into(), self.session).and_then(|m| { let msrc = self.session.load_file_and_mask_comments(&m.filepath); typeinf::get_type_of_match(m, msrc.as_src(), self.session) @@ -826,7 +830,7 @@ fn path_to_match_including_generics(ty: Ty, contextm: &Match, session: &Session) fn find_type_match_including_generics(fieldtype: &core::Ty, filepath: &Path, - pos: Point, + pos: BytePos, structm: &Match, session: &Session) -> Option{ assert_eq!(&structm.filepath, filepath); @@ -867,7 +871,7 @@ fn find_type_match_including_generics(fieldtype: &core::Ty, struct StructVisitor { pub scope: Scope, - pub fields: Vec<(String, Point, Option)> + pub fields: Vec<(String, BytePos, Option)> } impl<'ast> visit::Visitor<'ast> for StructVisitor { @@ -887,7 +891,7 @@ impl<'ast> visit::Visitor<'ast> for StructVisitor { None => format!("{}", self.fields.len()), }; - self.fields.push((name, point as usize, ty)); + self.fields.push((name, point.into(), ty)); } } } @@ -1051,7 +1055,7 @@ impl TraitBounds { let path_search = core::PathSearch { path: path, filepath: filepath.as_ref().to_path_buf(), - point: (point as i32 + offset) as Point, + point: BytePos::from((point as i32 + offset) as u32), }; Some(path_search) } else { @@ -1071,7 +1075,7 @@ pub struct TypeParameter { /// the name of type parameter declared in generics, like 'T' pub name: String, /// The point 'T' appears - pub point: usize, + pub point: BytePos, /// bounds pub bounds: TraitBounds, } @@ -1129,7 +1133,7 @@ impl GenericsArgs { TraitBounds::from_ty_param_bounds(&ty_param.bounds, &filepath, offset); Some(TypeParameter { name: param_name, - point: (point as i32 + offset) as Point, + point: BytePos::from((point as i32 + offset) as u32), bounds, }) } @@ -1161,7 +1165,7 @@ impl<'ast, P: AsRef> visit::Visitor<'ast> for GenericsVisitor

{ pub struct EnumVisitor { pub name: String, - pub values: Vec<(String, Point)> + pub values: Vec<(String, BytePos)> } impl<'ast> visit::Visitor<'ast> for EnumVisitor { @@ -1176,7 +1180,7 @@ impl<'ast> visit::Visitor<'ast> for EnumVisitor { for variant in &enum_definition.variants { let codemap::BytePos(point) = variant.span.lo(); - self.values.push((variant.node.ident.to_string(), point as usize)); + self.values.push((variant.node.ident.to_string(), point.into())); } } } @@ -1193,13 +1197,13 @@ pub fn parse_use(s: String) -> UseVisitor { v } -pub fn parse_pat_bind_stmt(s: String) -> Vec { +pub fn parse_pat_bind_stmt(s: String) -> Vec { let mut v = PatBindVisitor { ident_points: Vec::new() }; with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt)); v.ident_points } -pub fn parse_struct_fields(s: String, scope: Scope) -> Vec<(String, Point, Option)> { +pub fn parse_struct_fields(s: String, scope: Scope) -> Vec<(String, BytePos, Option)> { let mut v = StructVisitor { scope, fields: Vec::new() }; with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt)); v.fields @@ -1263,11 +1267,11 @@ pub fn parse_type(s: String) -> TypeVisitor { v } -pub fn parse_fn_args(s: String) -> Vec { +pub fn parse_fn_args(s: String) -> Vec { parse_pat_idents(s) } -pub fn parse_pat_idents(s: String) -> Vec { +pub fn parse_pat_idents(s: String) -> Vec { let mut v = PatVisitor{ ident_points: Vec::new() }; with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt)); debug!("ident points are {:?}", v.ident_points); @@ -1283,12 +1287,12 @@ pub fn parse_fn_output(s: String, scope: Scope) -> Option { pub fn parse_fn_arg_type( s: String, - argpos: Point, + argpos: BytePos, scope: Scope, session: &Session, offset: i32, ) -> Option { - debug!("parse_fn_arg {} |{}|", argpos, s); + debug!("parse_fn_arg {:?} |{}|", argpos, s); let mut v = FnArgTypeVisitor { argpos, scope, @@ -1319,7 +1323,7 @@ pub fn parse_enum(s: String) -> EnumVisitor { v } -pub fn get_type_of(s: String, fpath: &Path, pos: Point, session: &Session) -> Option { +pub fn get_type_of(s: String, fpath: &Path, pos: BytePos, session: &Session) -> Option { let startscope = Scope { filepath: fpath.to_path_buf(), point: pos @@ -1332,7 +1336,7 @@ pub fn get_type_of(s: String, fpath: &Path, pos: Point, session: &Session) -> Op } // pos points to an ident in the lhs of the stmtstr -pub fn get_let_type(s: String, pos: Point, scope: Scope, session: &Session) -> Option { +pub fn get_let_type(s: String, pos: BytePos, scope: Scope, session: &Session) -> Option { let mut v = LetTypeVisitor { scope, session, @@ -1344,7 +1348,7 @@ pub fn get_let_type(s: String, pos: Point, scope: Scope, session: &Session) -> O v.result } -pub fn get_match_arm_type(s: String, pos: Point, scope: Scope, session: &Session) -> Option { +pub fn get_match_arm_type(s: String, pos: BytePos, scope: Scope, session: &Session) -> Option { let mut v = MatchTypeVisitor { scope, session, @@ -1373,7 +1377,7 @@ impl<'ast> visit::Visitor<'ast> for FnOutputVisitor { /// Visitor to detect type of fnarg pub struct FnArgTypeVisitor<'c: 's, 's> { /// the code point arg appears in search string - argpos: Point, + argpos: BytePos, scope: Scope, session: &'s Session<'c>, generics: GenericsArgs, @@ -1400,7 +1404,7 @@ impl<'c, 's, 'ast> visit::Visitor<'ast> for FnArgTypeVisitor<'c, 's> { let filepath = &self.scope.filepath; self.result = fd.inputs .iter() - .find(|arg| point_is_in_span(self.argpos as u32, &arg.pat.span)) + .find(|arg| point_is_in_span(self.argpos, &arg.pat.span)) .and_then(|arg| { debug!("[FnArgTypeVisitor::visit_fn] type {:?} was found", arg.ty); let ty = to_racer_ty(&arg.ty, &self.scope) diff --git a/src/racer/codecleaner.rs b/src/racer/codecleaner.rs index 05fc7825..1bc19ed5 100644 --- a/src/racer/codecleaner.rs +++ b/src/racer/codecleaner.rs @@ -235,7 +235,7 @@ pub struct CommentSkipIterRev<'a> { } /// This produce CommentSkipIterRev for range [0, start) -pub fn comment_skip_iter_rev(s: &str, start: Point) -> CommentSkipIterRev { +pub fn comment_skip_iter_rev(s: &str, start: BytePos) -> CommentSkipIterRev { let start = if start > s.len() { 0 } else { start }; CommentSkipIterRev { src: s, pos: start } } diff --git a/src/racer/codeiter.rs b/src/racer/codeiter.rs index 4aeada94..614c8a17 100644 --- a/src/racer/codeiter.rs +++ b/src/racer/codeiter.rs @@ -1,23 +1,23 @@ use std::iter::{Fuse, Iterator}; -use core::{Point, SourceByteRange}; +use core::{BytePos, ByteRange}; pub struct StmtIndicesIter<'a,I> - where I: Iterator + where I: Iterator { src: &'a str, it: I, - pos: Point, - end: Point + pos: BytePos, + end: BytePos } impl<'a,I> Iterator for StmtIndicesIter<'a,I> - where I: Iterator + where I: Iterator { - type Item = SourceByteRange; + type Item = ByteRange; #[inline] - fn next(&mut self) -> Option { + fn next(&mut self) -> Option { let src_bytes = self.src.as_bytes(); let mut enddelim = b';'; let mut bracelevel = 0isize; @@ -32,40 +32,45 @@ impl<'a,I> Iterator for StmtIndicesIter<'a,I> if self.end == pos { // get the next chunk of code match self.it.next() { - Some((ch_start, ch_end)) => { - self.end = ch_end; - if start == pos { start = ch_start; } - pos = ch_start; + Some(ch_range) => { + self.end = ch_range.end; + if start == pos { + start = ch_range.start; + } + pos = ch_range.start; } None => { // no more chunks. finished self.pos = pos; - return if start < self.end { Some((start, self.end)) } - else { None } + if start < self.end { + return Some(ByteRange::new(start, self.end)); + } else { + return None; + } } } } if start == pos { // if this is a new stmt block, skip the whitespace - for &b in &src_bytes[pos..self.end] { + for &b in &src_bytes[pos.0..self.end.0] { match b { - b' ' | b'\r' | b'\n' | b'\t' => { pos += 1; }, + b' ' | b'\r' | b'\n' | b'\t' => { + pos += BytePos(1); + }, _ => { break; } } } start = pos; - // test attribute #[foo = bar] - if pos < self.end && src_bytes[pos] == b'#' { + if pos < self.end && src_bytes[pos.0] == b'#' { enddelim = b']' }; } // iterate through the chunk, looking for stmt end - for &b in &src_bytes[pos..self.end] { - pos += 1; - + for &b in &src_bytes[pos.0..self.end.0] { + pos += BytePos(1); match b { b'(' => { parenlevel += 1; }, b')' => { parenlevel -= 1; }, @@ -92,8 +97,8 @@ impl<'a,I> Iterator for StmtIndicesIter<'a,I> // macro if followed by at least one space or ( // FIXME: test with boolean 'not' expression if parenlevel == 0 && bracelevel == 0 - && pos < self.end && (pos-start) > 1 { - match src_bytes[pos] { + && pos < self.end && (pos - start).0 > 1 { + match src_bytes[pos.0] { b' ' | b'\r' | b'\n' | b'\t' | b'(' => { enddelim = b')'; }, @@ -107,31 +112,32 @@ impl<'a,I> Iterator for StmtIndicesIter<'a,I> || (enddelim == b && bracelevel == 0 && parenlevel == 0 && bracketlevel == 0) { self.pos = pos; - return Some((start, pos)); + return Some(ByteRange::new(start, pos)); } } } } } -fn is_a_use_stmt(src_bytes: &[u8], start: Point, pos: Point) -> bool { +fn is_a_use_stmt(src_bytes: &[u8], start: BytePos, pos: BytePos) -> bool { let whitespace = b" {\t\r\n"; - (pos > 3 && &src_bytes[start..start+3] == b"use" && - whitespace.contains(&src_bytes[start+3])) || - (pos > 7 && &src_bytes[start..(start+7)] == b"pub use" && - whitespace.contains(&src_bytes[start+7])) + (pos.0 > 3 && &src_bytes[start.0..start.0 + 3] == b"use" && + whitespace.contains(&src_bytes[start.0 + 3])) || + (pos.0 > 7 && &src_bytes[start.0..(start.0 + 7)] == b"pub use" && + whitespace.contains(&src_bytes[start.0 + 7])) } -fn is_a_let_stmt(src_bytes: &[u8], start: Point, pos: Point) -> bool { +fn is_a_let_stmt(src_bytes: &[u8], start: BytePos, pos: BytePos) -> bool { let whitespace = b" {\t\r\n"; - pos > 3 && &src_bytes[start..start+3] == b"let" && whitespace.contains(&src_bytes[start+3]) + pos.0 > 3 && &src_bytes[start.0..start.0 + 3] == b"let" + && whitespace.contains(&src_bytes[start.0 + 3]) } impl<'a, I> StmtIndicesIter<'a,I> - where I: Iterator + where I: Iterator { pub fn from_parts(src: &str, it: I) -> Fuse> { - StmtIndicesIter{ src, it, pos: 0, end: 0 }.fuse() + StmtIndicesIter{ src, it, pos: BytePos::zero(), end: BytePos::zero() }.fuse() } } diff --git a/src/racer/core.rs b/src/racer/core.rs index 677d98ee..dd107622 100644 --- a/src/racer/core.rs +++ b/src/racer/core.rs @@ -1,4 +1,6 @@ use cargo::core::Resolve; +use rls_span; +use syntax::codemap; use std::fs::File; use std::io::Read; use std::{vec, fmt}; @@ -6,7 +8,7 @@ use std::{str, path}; use std::io; use std::cell::RefCell; use std::collections::{hash_map, HashMap}; -use std::ops::Deref; +use std::ops::{Deref, Range}; use std::slice; use std::cmp::{min, max}; use std::iter::{Fuse, Iterator}; @@ -81,20 +83,95 @@ pub enum CompletionType { Path } -/// A byte offset in a file. -pub type Point = usize; +/// 0-based byte offset in a file. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash, + Index, From, Add, Sub, AddAssign, SubAssign)] +pub struct BytePos(pub usize); -/// A range of text between two positions. -pub type SourceByteRange = (Point, Point); +impl From for BytePos { + fn from(u: u32) -> Self { + BytePos(u as usize) + } +} + +impl BytePos { + pub fn zero() -> Self { + BytePos(0) + } + pub fn decrement(self) -> Self { + BytePos(self.0 - 1) + } + pub fn increment(self) -> Self { + BytePos(self.0 + 1) + } +} + +/// 0-based byte range in a file. +/// start: inclusive end: exclusive i.e. [start, end) +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)] +pub struct ByteRange { + pub start: BytePos, + pub end: BytePos, +} + +impl ByteRange { + pub fn new>(start: P, end: P) -> Self { + ByteRange { + start: start.into(), + end: end.into(), + } + } + pub fn len(&self) -> usize { + (self.end - self.start).0 + } + pub fn contains(&self, point: BytePos) -> bool { + self.start <= point && point < self.end + } + pub fn shift>(&self, shift: P) -> Self { + ByteRange { + start: self.start + shift.into(), + end: self.end + shift.into(), + } + } + pub fn to_range(&self) -> Range { + self.start.0..self.end.0 + } + pub fn to_tuple(&self) -> (BytePos, BytePos) { + (self.start, self.end) + } +} -/// Line and Column position in a file -#[derive(Clone, Debug, PartialEq, Eq, Copy)] +impl From for ByteRange { + fn from(span: codemap::Span) -> Self { + let (lo, hi) = ast::destruct_span(span); + ByteRange::new(lo, hi) + } +} + +/// Row and Column position in a file +// TODO: to use ZeroIndexed row is better? +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct Coordinate { - /// Line number, 1 based - pub line: usize, + pub row: rls_span::Row, + pub col: rls_span::Column, +} - /// Column number - 1 based - pub column: usize, +impl Coordinate { + pub fn new(row: u32, col: u32) -> Self { + Coordinate { + row: rls_span::Row::::new_one_indexed(row), + col: rls_span::Column::::new_zero_indexed(col), + } + } + pub fn from_zero_indexed(row: u32, col: u32) -> Self { + Coordinate { + row: rls_span::Row::::new_zero_indexed(row).one_indexed(), + col: rls_span::Column::::new_zero_indexed(col), + } + } + pub fn start() -> Self { + Coordinate::new(0, 1) + } } /// Context, source, and etc. for detected completion or definition @@ -102,7 +179,7 @@ pub struct Coordinate { pub struct Match { pub matchstr: String, pub filepath: path::PathBuf, - pub point: Point, + pub point: BytePos, pub coords: Option, pub local: bool, pub mtype: MatchType, @@ -129,14 +206,13 @@ impl Match { #[derive(Debug, Clone, Copy)] pub enum Location { /// A byte offset in the file - Point(Point), - + Point(BytePos), /// 1-based line and column indices. Coords(Coordinate), } -impl From for Location { - fn from(val: Point) -> Location { +impl From for Location { + fn from(val: BytePos) -> Location { Location::Point(val) } } @@ -149,12 +225,12 @@ impl From for Location { /// Internal cursor methods pub trait LocationExt { - fn to_point(&self, src: &IndexedSource) -> Option; + fn to_point(&self, src: &IndexedSource) -> Option; fn to_coords(&self, src: &IndexedSource) -> Option; } impl LocationExt for Location { - fn to_point(&self, src: &IndexedSource) -> Option { + fn to_point(&self, src: &IndexedSource) -> Option { match *self { Location::Point(val) => Some(val), Location::Coords(ref coords) => { @@ -189,7 +265,7 @@ impl fmt::Debug for Match { #[derive(Clone)] pub struct Scope { pub filepath: path::PathBuf, - pub point: Point + pub point: BytePos } impl Scope { @@ -375,7 +451,7 @@ impl From for PathSegment { pub struct PathSearch { pub path: Path, pub filepath: path::PathBuf, - pub point: Point + pub point: BytePos } impl fmt::Debug for PathSearch { @@ -389,15 +465,14 @@ impl fmt::Debug for PathSearch { pub struct IndexedSource { pub code: String, - pub idx: Vec, - pub lines: RefCell> + pub idx: Vec, + pub lines: RefCell> } #[derive(Clone,Copy)] pub struct Src<'c> { pub src: &'c IndexedSource, - pub from: Point, - pub to: Point + pub range: ByteRange, } impl IndexedSource { @@ -409,7 +484,7 @@ impl IndexedSource { lines: RefCell::new(Vec::new()) } } - + pub fn with_src(&self, new_src: String) -> IndexedSource { IndexedSource { code: new_src, @@ -419,48 +494,50 @@ impl IndexedSource { } pub fn as_src(&self) -> Src { - self.from(0) + self.src_from_start(BytePos::zero()) } - pub fn from(&self, from: Point) -> Src { + pub fn src_from_start(&self, new_start: BytePos) -> Src { Src { src: self, - from: from, - to: self.len() + range: ByteRange::new(new_start, self.len().into()) } } fn cache_lineoffsets(&self) { - if self.lines.borrow().len() == 0 { - let mut result = Vec::new(); - let mut point = 0; - for line in self.code.split('\n') { - result.push((point, line.len() + 1)); - point += line.len() + 1; - } - *self.lines.borrow_mut() = result; + if self.lines.borrow().len() != 0 { + return; } + let mut before = 0; + *self.lines.borrow_mut() = self.code.lines().map(|line| { + let len = line.len() + 1; + let res = ByteRange::new(before, before + len); + before += len; + res + }).collect(); } - pub fn coords_to_point(&self, coords: &Coordinate) -> Option { + pub fn coords_to_point(&self, coords: &Coordinate) -> Option { self.cache_lineoffsets(); self.lines .borrow() - .get(coords.line - 1) - .and_then(|&(i, l)| { - if coords.column <= l { - Some(i + coords.column) + .get(coords.row.zero_indexed().0 as usize) + .and_then(|&range| { + let col = coords.col.0 as usize; + if col < range.len() { + Some(range.start + col.into()) } else { None } }) } - pub fn point_to_coords(&self, point: Point) -> Option { + pub fn point_to_coords(&self, point: BytePos) -> Option { self.cache_lineoffsets(); - for (n, &(i, l)) in self.lines.borrow().iter().enumerate() { - if i <= point && (point - i) <= l { - return Some(Coordinate { line: n + 1, column: point - i }); + // TODO: binary search is better? + for (i, &range) in self.lines.borrow().iter().enumerate() { + if range.contains(point) { + return Some(Coordinate::from_zero_indexed(i as u32, (point - range.start).0 as u32)) } } None @@ -484,7 +561,6 @@ impl<'c> Iterator for MatchIter<'c> { let src = self.session.load_file(m.filepath.as_path()); m.coords = src.point_to_coords(point); } - m }) @@ -527,32 +603,38 @@ fn myfn(b:usize) { } + impl<'c> Src<'c> { + pub fn start(&self) -> BytePos { + self.range.start + } + + pub fn end(&self) -> BytePos { + self.range.end + } + pub fn iter_stmts(&self) -> Fuse> { StmtIndicesIter::from_parts(self, self.chunk_indices()) } - pub fn from(&self, from: Point) -> Src<'c> { + pub fn shift_start(&self, shift: BytePos) -> Src<'c> { Src { src: self.src, - from: self.from + from, - to: self.to + range: ByteRange::new(self.range.start + shift, self.range.end), } } - pub fn to(&self, to: Point) -> Src<'c> { + pub fn change_length(&self, new_length: BytePos) -> Src<'c> { Src { src: self.src, - from: self.from, - to: self.from + to + range: ByteRange::new(self.range.start, self.range.start + new_length), } } - pub fn from_to(&self, from: Point, to: Point) -> Src<'c> { + pub fn shift_range(&self, new_range: ByteRange) -> Src<'c> { Src { src: self.src, - from: self.from + from, - to: self.from + to + range: new_range.shift(self.range.start), } } @@ -565,26 +647,28 @@ impl<'c> Src<'c> { // N.b. src can be a substr, so iteration skips chunks that aren't part of the substr pub struct CodeChunkIter<'c> { src: Src<'c>, - iter: slice::Iter<'c, SourceByteRange> + iter: slice::Iter<'c, ByteRange> } impl<'c> Iterator for CodeChunkIter<'c> { - type Item = SourceByteRange; + type Item = ByteRange; - fn next(&mut self) -> Option { + fn next(&mut self) -> Option { loop { match self.iter.next() { None => return None, - Some(&(start, end)) => { - if end < self.src.from { + Some(&range) => { + let (start, end) = (range.start, range.end); + if end < self.src.start() { continue; } - if start > self.src.to { + if start > self.src.end() { return None; } else { - return Some(( - max(start, self.src.from) - self.src.from, - min(end, self.src.to) - self.src.from)); + return Some(ByteRange::new( + max(start, self.src.start()) - self.src.start(), + min(end, self.src.end()) - self.src.start() + )); } } } @@ -602,7 +686,7 @@ impl Deref for IndexedSource { impl<'c> Deref for Src<'c> { type Target = str; fn deref(&self) -> &str { - &self.src.code[self.from..self.to] + &self.src.code[self.range.to_range()] } } @@ -774,8 +858,8 @@ pub struct Session<'c> { /// borrowed here in order to support reuse across Racer operations. cache: &'c FileCache, /// Cache for generic impls - pub generic_impls: RefCell>>>, /// Cached dependencie (path to Cargo.toml -> Depedencies) cached_deps: RefCell>>, @@ -906,7 +990,7 @@ pub fn to_point

( coords: Coordinate, path: P, session: &Session -) -> Option +) -> Option where P: AsRef { Location::from(coords).to_point(&session.load_file(path.as_ref())) @@ -914,7 +998,7 @@ pub fn to_point

( /// Get the racer point of a line/character number pair for a file. pub fn to_coords

( - point: Point, + point: BytePos, path: P, session: &Session ) -> Option diff --git a/src/racer/lib.rs b/src/racer/lib.rs index 81e27a4d..5b649a87 100644 --- a/src/racer/lib.rs +++ b/src/racer/lib.rs @@ -1,12 +1,18 @@ #![feature(iterator_flatten)] +#![feature(slice_get_slice)] #![cfg_attr(all(feature = "clippy", not(test)), deny(print_stdout))] #![cfg_attr(feature = "nightly", feature(test))] -#[macro_use] extern crate log; -#[macro_use] extern crate lazy_static; +#[macro_use] +extern crate log; +#[macro_use] +extern crate lazy_static; extern crate cargo; extern crate syntax; +#[macro_use] +extern crate derive_more; +extern crate rls_span; #[macro_use] mod testutils; diff --git a/src/racer/matchers.rs b/src/racer/matchers.rs index 65283767..cfeb24b4 100644 --- a/src/racer/matchers.rs +++ b/src/racer/matchers.rs @@ -1,5 +1,5 @@ use {scopes, typeinf, ast}; -use core::{Match, PathSegment, Src, Session, Coordinate, SessionExt, Point}; +use core::{Match, PathSegment, Src, Session, Coordinate, SessionExt, BytePos, ByteRange}; use util::{StackLinkedListNode, symbol_matches, txt_matches, find_ident_end, is_ident_char, char_at}; use fileres::{get_crate_file, get_module_file}; use nameres::resolve_path; @@ -7,25 +7,35 @@ use core::SearchType::{self, StartsWith, ExactMatch}; use core::MatchType::{self, Let, Module, Function, Struct, Type, Trait, Enum, EnumVariant, Const, Static, IfLet, WhileLet, For, Macro}; use core::Namespace; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::{str, vec}; +use std::collections::HashSet; /// The location of an import (`use` item) currently being resolved. #[derive(PartialEq, Eq)] pub struct PendingImport<'fp> { filepath: &'fp Path, - blobstart: Point, - blobend: Point, + blob_range: ByteRange, } /// A stack of imports (`use` items) currently being resolved. pub type PendingImports<'stack, 'fp> = StackLinkedListNode<'stack, PendingImport<'fp>>; -pub fn match_types(src: Src, blobstart: Point, blobend: Point, - searchstr: &str, filepath: &Path, - search_type: SearchType, - local: bool, session: &Session, - pending_imports: &PendingImports) -> impl Iterator { +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct MatchCxt<'s, 'p> { + pub filepath: &'p Path, + pub search_str: &'s str, + pub range: ByteRange, + pub search_type: SearchType, + pub is_local: bool, +} + +pub fn match_types( + src: Src, + context: &MatchCxt, + session: &Session, + pending_imports: &PendingImports +) -> impl Iterator { match_extern_crate(&src, blobstart, blobend, searchstr, filepath, search_type, session).into_iter() .chain(match_mod(src, blobstart, blobend, searchstr, filepath, search_type, local, session)) .chain(match_struct(&src, blobstart, blobend, searchstr, filepath, search_type, local)) @@ -35,16 +45,14 @@ pub fn match_types(src: Src, blobstart: Point, blobend: Point, .chain(match_use(&src, blobstart, blobend, searchstr, filepath, search_type, local, session, pending_imports)) } -pub fn match_values(src: Src, blobstart: Point, blobend: Point, - searchstr: &str, filepath: &Path, search_type: SearchType, - local: bool) -> impl Iterator { +pub fn match_values(src: Src, context: &MatchCxt) -> impl Iterator { match_const(&src, blobstart, blobend, searchstr, filepath, search_type, local).into_iter() .chain(match_static(&src, blobstart, blobend, searchstr, filepath, search_type, local)) .chain(match_fn(&src, blobstart, blobend, searchstr, filepath, search_type, local)) .chain(match_macro(&src, blobstart, blobend, searchstr, filepath, search_type, local)) } -fn strip_keyword_prefix(src: &str, keyword: &str) -> Option { +fn strip_keyword_prefix(src: &str, keyword: &str) -> Option { let mut start = 0usize; if src.starts_with(keyword) { // Rust added support for `pub(in codegen)`; we need to consume the visibility @@ -72,35 +80,34 @@ fn strip_keyword_prefix(src: &str, keyword: &str) -> Option { _ => break } } - if start != oldstart { return Some(start); } + if start != oldstart { return Some(start.into()); } } None } -fn find_keyword(src: &str, pattern: &str, search: &str, search_type: SearchType, local: bool) --> Option { +fn find_keyword(src: &str, pattern: &str, context: &MatchCxt) -> Option { // search for "^(pub\s+)?(unsafe\s+)?pattern\s+search" // if not local must start with pub - if !local && !src.starts_with("pub") { return None; } + if !context.is_local && !src.starts_with("pub") { return None; } - let mut start = 0usize; + let mut start = BytePos::zero(); // optional (pub\s+)?(unsafe\s+)? for pat in &["pub", "unsafe"] { - if let Some(prefix_len) = strip_keyword_prefix(&src[start..], pat) { + if let Some(prefix_len) = strip_keyword_prefix(&src[start.0..], pat) { start += prefix_len; } } // mandatory pattern\s+ - if src[start..].starts_with(pattern) { + if src[start.0..].starts_with(pattern) { // remove whitespaces ... must have one at least - start += pattern.len(); + start += pattern.len().into(); let oldstart = start; - for &b in src[start..].as_bytes() { + for &b in src[start.0..].as_bytes() { match b { - b' '|b'\r'|b'\n'|b'\t' => start += 1, + b' '|b'\r'|b'\n'|b'\t' => start = start.increment(), _ => break } } @@ -109,12 +116,13 @@ fn find_keyword(src: &str, pattern: &str, search: &str, search_type: SearchType, return None; } - if src[start..].starts_with(search) { - match search_type { + let search_str_len = context.search_str.len(); + if src[start.0..].starts_with(context.search_str) { + match context.search_type { StartsWith => Some(start), ExactMatch => { - if src.len() > start+search.len() && - !is_ident_char(char_at(src, start + search.len())) { + if src.len() > start.0 + search_str_len && + !is_ident_char(char_at(src, start.0 + search_str_len)) { Some(start) } else { None @@ -126,26 +134,29 @@ fn find_keyword(src: &str, pattern: &str, search: &str, search_type: SearchType, } } -fn is_const_fn(src: &str, blobstart: Point, blobend: Point) -> bool { - src[blobstart..blobend].contains("const fn") +fn is_const_fn(src: &str, blob_range: ByteRange) -> bool { + src[blob_range.to_range()].contains("const fn") } -fn match_pattern_start(src: &str, blobstart: Point, blobend: Point, - searchstr: &str, filepath: &Path, search_type: SearchType, - local: bool, pattern: &str, mtype: MatchType) -> Option { +fn match_pattern_start( + src: &str, + context: &MatchCxt, + pattern: &str, + mtype: MatchType +) -> Option { // ast currently doesn't contain the ident coords, so match them with a hacky // string search - let blob = &src[blobstart..blobend]; - if let Some(start) = find_keyword(blob, pattern, searchstr, search_type, local) { - if let Some(end) = blob[start..].find(':') { - let s = blob[start..start+end].trim_right(); + let blob = &src[context.range.to_range()]; + if let Some(start) = find_keyword(blob, pattern, context) { + if let Some(end) = blob[start.0..].find(':') { + let s = blob[start.0..start.0 + end].trim_right(); return Some(Match { matchstr: s.to_owned(), - filepath: filepath.to_path_buf(), - point: blobstart+start, + filepath: context.filepath.to_path_buf(), + point: context.range.start + start, coords: None, - local: local, + local: context.is_local, mtype: mtype, contextstr: first_line(blob), generic_args: Vec::new(), @@ -157,46 +168,40 @@ fn match_pattern_start(src: &str, blobstart: Point, blobend: Point, None } -pub fn match_const(msrc: &str, blobstart: Point, blobend: Point, - searchstr: &str, filepath: &Path, search_type: SearchType, - local: bool) -> Option { - if is_const_fn(msrc, blobstart, blobend) { +pub fn match_const(msrc: &str, context: &MatchCxt) -> Option { + if is_const_fn(msrc, context.range) { return None; } - match_pattern_start(msrc, blobstart, blobend, searchstr, filepath, - search_type, local, "const", Const) + match_pattern_start(msrc, context, "const", Const) } -pub fn match_static(msrc: &str, blobstart: Point, blobend: Point, - searchstr: &str, filepath: &Path, search_type: SearchType, - local: bool) -> Option { - match_pattern_start(msrc, blobstart, blobend, searchstr, filepath, - search_type, local, "static", Static) +pub fn match_static(msrc: &str, context: &MatchCxt) -> Option { + match_pattern_start(msrc, context, "static", Static) } -fn match_pattern_let(msrc: &str, blobstart: Point, blobend: Point, - searchstr: &str, filepath: &Path, search_type: SearchType, - local: bool, pattern: &str, mtype: MatchType) -> Vec { +fn match_pattern_let(msrc: &str, context: &MatchCxt, pattern: &str, mtype: MatchType) -> Vec { let mut out = Vec::new(); - let blob = &msrc[blobstart..blobend]; - if blob.starts_with(pattern) && txt_matches(search_type, searchstr, blob) { + let blob = &msrc[context.range.to_range()]; + if blob.starts_with(pattern) && txt_matches(context.search_type, context.search_str, blob) { let coords = ast::parse_pat_bind_stmt(blob.to_owned()); - for (start, end) in coords { - let s = &blob[start..end]; - if symbol_matches(search_type, searchstr, s) { - debug!("match_pattern_let point is {}", blobstart + start); - out.push(Match { matchstr: s.to_owned(), - filepath: filepath.to_path_buf(), - point: blobstart + start, - coords: None, - local: local, - mtype: mtype.clone(), - contextstr: first_line(blob), - generic_args: Vec::new(), - generic_types: Vec::new(), - docs: String::new(), - }); - if let ExactMatch = search_type { + for pat_range in coords { + let s = &blob[pat_range.to_range()]; + if symbol_matches(context.search_type, context.search_str, s) { + let start = context.range.start + pat_range.start; + debug!("match_pattern_let point is {:?}", start); + out.push(Match { + matchstr: s.to_owned(), + filepath: context.filepath.to_path_buf(), + point: start, + coords: None, + local: context.is_local, + mtype: mtype.clone(), + contextstr: first_line(blob), + generic_args: Vec::new(), + generic_types: Vec::new(), + docs: String::new(), + }); + if context.search_type == ExactMatch { break; } } @@ -205,25 +210,16 @@ fn match_pattern_let(msrc: &str, blobstart: Point, blobend: Point, out } -pub fn match_if_let(msrc: &str, blobstart: Point, blobend: Point, - searchstr: &str, filepath: &Path, search_type: SearchType, - local: bool) -> Vec { - match_pattern_let(msrc, blobstart, blobend, searchstr, filepath, - search_type, local, "if let ", IfLet) +pub fn match_if_let(msrc: &str, context: &MatchCxt) -> Vec { + match_pattern_let(msrc, context, "if let ", IfLet) } -pub fn match_while_let(msrc: &str, blobstart: Point, blobend: Point, - searchstr: &str, filepath: &Path, search_type: SearchType, - local: bool) -> Vec { - match_pattern_let(msrc, blobstart, blobend, searchstr, filepath, - search_type, local, "while let ", WhileLet) +pub fn match_while_let(msrc: &str, context: &MatchCxt) -> Vec { + match_pattern_let(msrc, context, "while let ", WhileLet) } -pub fn match_let(msrc: &str, blobstart: Point, blobend: Point, - searchstr: &str, filepath: &Path, search_type: SearchType, - local: bool) -> Vec { - match_pattern_let(msrc, blobstart, blobend, searchstr, filepath, - search_type, local, "let ", Let) +pub fn match_let(msrc: &str, context: &MatchCxt) -> Vec { + match_pattern_let(msrc, context, "let ", Let) } pub fn match_for(msrc: &str, blobstart: Point, blobend: Point, diff --git a/src/racer/nameres.rs b/src/racer/nameres.rs index beb5fa5f..1eb9120e 100644 --- a/src/racer/nameres.rs +++ b/src/racer/nameres.rs @@ -1,4 +1,4 @@ -// Name resolution +//! Name resolution use std::{self, vec}; use std::collections::HashSet; @@ -7,7 +7,7 @@ use std::path::{Path, PathBuf}; use {core, ast, matchers, scopes, typeinf}; use core::SearchType::{self, ExactMatch, StartsWith}; -use core::{Match, Src, Session, Coordinate, SessionExt, Ty, Point}; +use core::{Match, Src, Session, Coordinate, SessionExt, Ty, BytePos}; use core::MatchType::{Module, Function, Struct, Enum, EnumVariant, FnArg, Trait, StructField, Impl, TraitImpl, MatchArm, Builtin, TypeParameter}; use core::Namespace; @@ -26,7 +26,7 @@ fn search_struct_fields(searchstr: &str, structmatch: &Match, search_type: SearchType, session: &Session) -> vec::IntoIter { let src = session.load_file(&structmatch.filepath); let struct_start = scopes::expect_stmt_start(src.as_src(), structmatch.point); - let structsrc = scopes::end_of_next_scope(&src[struct_start..]); + let structsrc = scopes::end_of_next_scope(&src[struct_start.0..]); let fields = ast::parse_struct_fields(structsrc.to_owned(), core::Scope::from_match(structmatch)); @@ -57,11 +57,15 @@ fn search_struct_fields(searchstr: &str, structmatch: &Match, out.into_iter() } -pub fn search_for_impl_methods(match_request: &Match, - fieldsearchstr: &str, point: Point, - fpath: &Path, local: bool, - search_type: SearchType, - session: &Session) -> vec::IntoIter { +pub fn search_for_impl_methods( + match_request: &Match, + fieldsearchstr: &str, + point: BytePos, + fpath: &Path, + local: bool, + search_type: SearchType, + session: &Session, +) -> vec::IntoIter { let implsearchstr: &str = &match_request.matchstr; @@ -79,8 +83,8 @@ pub fn search_for_impl_methods(match_request: &Match, let src = session.load_file(&m.filepath); // find the opening brace and skip to it. - if let Some(n) = src[m.point..].find('{') { - let point = m.point + n + 1; + if let Some(n) = src[m.point.0..].find('{') { + let point = m.point.increment() + n.into(); for m in search_scope_for_methods(point, src.as_src(), fieldsearchstr, &m.filepath, search_type) { out.push(m); } @@ -89,8 +93,8 @@ pub fn search_for_impl_methods(match_request: &Match, debug!("found generic impl!! {:?}", gen_m); let src = session.load_file(&gen_m.filepath); // find the opening brace and skip to it. - if let Some(n) = src[gen_m.point..].find('{') { - let point = gen_m.point + n + 1; + if let Some(n) = src[gen_m.point.0..].find('{') { + let point = gen_m.point.increment() + n.into(); for gen_method in search_generic_impl_scope_for_methods(point, src.as_src(), fieldsearchstr, &gen_m, search_type) { out.push(gen_method); @@ -107,8 +111,8 @@ pub fn search_for_impl_methods(match_request: &Match, debug!("found generic impl!! {:?}", m); let src = session.load_file(&m.filepath); // find the opening brace and skip to it. - if let Some(n) = src[m.point..].find('{') { - let point = m.point + n + 1; + if let Some(n) = src[m.point.0..].find('{') { + let point = m.point.increment() + n.into(); for m in search_generic_impl_scope_for_methods(point, src.as_src(), fieldsearchstr, &m, search_type) { out.push(m); } @@ -120,36 +124,41 @@ pub fn search_for_impl_methods(match_request: &Match, out.into_iter() } -fn search_scope_for_methods(point: Point, src: Src, searchstr: &str, filepath: &Path, - search_type: SearchType) -> vec::IntoIter { - debug!("searching scope for methods {} |{}| {:?}", point, searchstr, filepath.display()); +fn search_scope_for_methods( + point: BytePos, + src: Src, + searchstr: &str, + filepath: &Path, + search_type: SearchType, +) -> vec::IntoIter { + debug!("searching scope for methods {:?} |{}| {:?}", point, searchstr, filepath.display()); - let scopesrc = src.from(point); + let scopesrc = src.shift_start(point); let mut out = Vec::new(); - for (blobstart,blobend) in scopesrc.iter_stmts() { - let blob = &scopesrc[blobstart..blobend]; + for blob_range in scopesrc.iter_stmts() { + let blob = &scopesrc[blob_range.to_range()]; if let Some(n) = blob.find(|c| {c == '{' || c == ';'}) { let signature = blob[..n].trim_right(); if txt_matches(search_type, &format!("fn {}", searchstr), signature) && typeinf::first_param_is_self(blob) { debug!("found a method starting |{}| |{}|", searchstr, blob); - // TODO: parse this properly - let start = blob.find(&format!("fn {}", searchstr)).unwrap() + 3; + // TODO: parse this properly, or, txt_matches should return match pos? + let start = BytePos::from(blob.find(&format!("fn {}", searchstr)).unwrap() + 3); let end = find_ident_end(blob, start); - let l = &blob[start..end]; + let l = &blob[start.0..end.0]; // TODO: make a better context string for functions let m = Match { matchstr: l.to_owned(), filepath: filepath.to_path_buf(), - point: point + blobstart + start, + point: point + blob_range.start + start, coords: None, local: true, mtype: Function, contextstr: signature.to_owned(), generic_args: Vec::new(), generic_types: Vec::new(), - docs: find_doc(&scopesrc, blobstart + start), + docs: find_doc(&scopesrc, blob_range.start + start), }; out.push(m); } @@ -158,36 +167,41 @@ fn search_scope_for_methods(point: Point, src: Src, searchstr: &str, filepath: & out.into_iter() } -fn search_generic_impl_scope_for_methods(point: Point, src: Src, searchstr: &str, contextm: &Match, - search_type: SearchType) -> vec::IntoIter { - debug!("searching generic impl scope for methods {} |{}| {:?}", point, searchstr, contextm.filepath.display()); +fn search_generic_impl_scope_for_methods( + point: BytePos, + src: Src, + searchstr: &str, + contextm: &Match, + search_type: SearchType, +) -> vec::IntoIter { + debug!("searching generic impl scope for methods {:?} |{}| {:?}", point, searchstr, contextm.filepath.display()); - let scopesrc = src.from(point); + let scopesrc = src.shift_start(point); let mut out = Vec::new(); - for (blobstart,blobend) in scopesrc.iter_stmts() { - let blob = &scopesrc[blobstart..blobend]; + for blob_range in scopesrc.iter_stmts() { + let blob = &scopesrc[blob_range.to_range()]; if let Some(n) = blob.find(|c| {c == '{' || c == ';'}) { let signature = blob[..n].trim_right(); if txt_matches(search_type, &format!("fn {}", searchstr), signature) && typeinf::first_param_is_self(blob) { debug!("found a method starting |{}| |{}|", searchstr, blob); - // TODO: parse this properly - let start = blob.find(&format!("fn {}", searchstr)).unwrap() + 3; + // TODO: parse this properly, or, txt_matches should return match pos? + let start = BytePos::from(blob.find(&format!("fn {}", searchstr)).unwrap() + 3); let end = find_ident_end(blob, start); - let l = &blob[start..end]; + let l = &blob[start.0..end.0]; // TODO: make a better context string for functions let m = Match { matchstr: l.to_owned(), filepath: contextm.filepath.clone(), - point: point + blobstart + start, + point: point + blob_range.start + start, coords: None, local: true, mtype: Function, contextstr: signature.to_owned(), generic_args: contextm.generic_args.clone(), // Attach impl generic args generic_types: contextm.generic_types.clone(), // Attach impl generic types - docs: find_doc(&scopesrc, blobstart + start), + docs: find_doc(&scopesrc, blob_range.start + start), }; out.push(m); } @@ -198,14 +212,19 @@ fn search_generic_impl_scope_for_methods(point: Point, src: Src, searchstr: &str /// Look for static trait functions. This fn doesn't search for _method_ declarations /// or implementations as `search_scope_for_methods` already handles that. -fn search_scope_for_static_trait_fns(point: Point, src: Src, searchstr: &str, filepath: &Path, - search_type: SearchType) -> vec::IntoIter { - debug!("searching scope for trait fn declarations {} |{}| {:?}", point, searchstr, filepath.display()); +fn search_scope_for_static_trait_fns( + point: BytePos, + src: Src, + searchstr: &str, + filepath: &Path, + search_type: SearchType, +) -> vec::IntoIter { + debug!("searching scope for trait fn declarations {:?} |{}| {:?}", point, searchstr, filepath.display()); - let scopesrc = src.from(point); + let scopesrc = src.shift_start(point); let mut out = Vec::new(); - for (blobstart,blobend) in scopesrc.iter_stmts() { - let blob = &scopesrc[blobstart..blobend]; + for blob_range in scopesrc.iter_stmts() { + let blob = &scopesrc[blob_range.to_range()]; if let Some(n) = blob.find(|c| c == '{' || c == ';') { let signature = blob[..n].trim_right(); @@ -215,21 +234,22 @@ fn search_scope_for_static_trait_fns(point: Point, src: Src, searchstr: &str, fi && !typeinf::first_param_is_self(blob) { debug!("found a method starting |{}| |{}|", searchstr, blob); // TODO: parse this properly - let start = blob.find(&format!("fn {}", searchstr)).unwrap() + 3; + // TODO: parse this properly, or, txt_matches should return match pos? + let start = BytePos::from(blob.find(&format!("fn {}", searchstr)).unwrap() + 3); let end = find_ident_end(blob, start); - let l = &blob[start..end]; + let l = &blob[start.0..end.0]; // TODO: make a better context string for functions let m = Match { matchstr: l.to_owned(), filepath: filepath.to_path_buf(), - point: point + blobstart + start, + point: point + blob_range.start + start, coords: None, local: true, mtype: Function, contextstr: signature.to_owned(), generic_args: Vec::new(), generic_types: Vec::new(), - docs: find_doc(&scopesrc, blobstart + start), + docs: find_doc(&scopesrc, blob_range.start + start), }; out.push(m); } @@ -239,23 +259,30 @@ fn search_scope_for_static_trait_fns(point: Point, src: Src, searchstr: &str, fi } -pub fn search_for_impls(pos: Point, searchstr: &str, filepath: &Path, local: bool, include_traits: bool, - session: &Session, pending_imports: &PendingImports) -> vec::IntoIter { - debug!("search_for_impls {}, {}, {:?}", pos, searchstr, filepath.display()); +pub fn search_for_impls( + pos: BytePos, + searchstr: &str, + filepath: &Path, + local: bool, + include_traits: bool, + session: &Session, + pending_imports: &PendingImports, +) -> vec::IntoIter { + debug!("search_for_impls {:?}, {}, {:?}", pos, searchstr, filepath.display()); let s = session.load_file(filepath); let scope_start = scopes::scope_start(s.as_src(), pos); - let src = s.from(scope_start); + let src = s.src_from_start(scope_start); let mut out = Vec::new(); - for (start, end) in src.iter_stmts() { - let blob = &src[start..end]; + for blob_range in src.iter_stmts() { + let blob = &src[blob_range.to_range()]; if blob.starts_with("impl") { blob.find('{').map(|n| { let decl = &blob[..n+1]; if decl.contains('!') { // Guard against macros - debug!("impl was probably a macro: {} {}", filepath.display(), start); + debug!("impl was probably a macro: {} {:?}", filepath.display(), blob_range.start); return; } let mut decl = decl.to_owned(); @@ -272,7 +299,7 @@ pub fn search_for_impls(pos: Point, searchstr: &str, filepath: &Path, local: boo let m = Match { matchstr: name.name.clone(), filepath: filepath.to_path_buf(), - point: scope_start + start + 5, + point: scope_start + blob_range.start + BytePos(5), // 5 = "impl ".len() coords: None, // items in trait impls have no "pub" but are // still accessible from other modules @@ -291,9 +318,15 @@ pub fn search_for_impls(pos: Point, searchstr: &str, filepath: &Path, local: boo // find trait if include_traits && is_trait_impl { let trait_path = implres.trait_path.unwrap(); - let m = resolve_path(&trait_path, - filepath, scope_start + start, ExactMatch, Namespace::Type, - session, pending_imports).nth(0); + let m = resolve_path( + &trait_path, + filepath, + scope_start + blob_range.start, + ExactMatch, + Namespace::Type, + session, + pending_imports + ).nth(0); debug!("found trait |{:?}| {:?}", trait_path, m); if let Some(mut m) = m { @@ -322,29 +355,35 @@ pub fn search_for_impls(pos: Point, searchstr: &str, filepath: &Path, local: boo out.into_iter() } -fn cached_generic_impls(filepath: &Path, session: &Session, scope_start: usize) - -> Rc> { +fn cached_generic_impls( + filepath: &Path, + session: &Session, + scope_start: BytePos, +) -> Rc> { // the cache is keyed by path and the scope we search in - session.generic_impls.borrow_mut().entry((filepath.into(), scope_start)) + session + .generic_impls + .borrow_mut() + .entry((filepath.into(), scope_start)) .or_insert_with(|| { let s = session.load_file(&filepath); - let src = s.from(scope_start); + let src = s.src_from_start(scope_start); let mut out = Vec::new(); - for (start, end) in src.iter_stmts() { - let blob = &src[start..end]; + for blob_range in src.iter_stmts() { + let blob = &src[blob_range.to_range()]; if blob.starts_with("impl") { blob.find('{').map(|n| { let decl = &blob[..n+1]; if decl.contains('!') { // Guard against macros - debug!("impl was probably a macro: {} {}", filepath.display(), start); + debug!("impl was probably a macro: {} {:?}", filepath.display(), blob_range.start); return; } let mut decl = decl.to_owned(); decl.push_str("}"); let (generics, impls) = ast::parse_generics_and_impl(decl, filepath); - out.push((start, blob.into(), generics, impls)); + out.push((blob_range.start, blob.into(), generics, impls)); }); } } @@ -353,8 +392,14 @@ fn cached_generic_impls(filepath: &Path, session: &Session, scope_start: usize) .clone() } -pub fn search_for_generic_impls(pos: Point, searchstr: &str, contextm: &Match, filepath: &Path, session: &Session) -> vec::IntoIter { - debug!("search_for_generic_impls {}, {}, {:?}", pos, searchstr, filepath.display()); +pub fn search_for_generic_impls( + pos: BytePos, + searchstr: &str, + contextm: &Match, + filepath: &Path, + session: &Session +) -> vec::IntoIter { + debug!("search_for_generic_impls {:?}, {}, {:?}", pos, searchstr, filepath.display()); let s = session.load_file(filepath); let scope_start = scopes::scope_start(s.as_src(), pos); @@ -367,7 +412,10 @@ pub fn search_for_generic_impls(pos: Point, searchstr: &str, contextm: &Match, f && gen_arg.bounds.find_by_name(searchstr).is_some() { debug!("generic impl decl {}", blob); - let trait_pos = blob.find(&trait_name.name).unwrap(); + let trait_pos: BytePos = blob + .find(&trait_name.name) + .expect("[search_for_generic_impls] trait was not found") + .into(); let self_path = core::Path::from_vec(false, vec![&contextm.matchstr]); let self_pathsearch = core::PathSearch { path: self_path, @@ -397,12 +445,18 @@ pub fn search_for_generic_impls(pos: Point, searchstr: &str, contextm: &Match, f } // scope headers include fn decls, if let, while let etc.. -fn search_scope_headers(point: Point, scopestart: Point, msrc: Src, searchstr: &str, - filepath: &Path, search_type: SearchType) -> vec::IntoIter { - debug!("search_scope_headers for |{}| pt: {}", searchstr, scopestart); +fn search_scope_headers( + point: BytePos, + scopestart: BytePos, + msrc: Src, + searchstr: &str, + filepath: &Path, + search_type: SearchType +) -> vec::IntoIter { + debug!("search_scope_headers for |{}| pt: {:?}", searchstr, scopestart); if let Some(stmtstart) = scopes::find_stmt_start(msrc, scopestart) { - let preblock = &msrc[stmtstart..scopestart]; + let preblock = &msrc[stmtstart.0..scopestart.0]; debug!("search_scope_headers preblock is |{}|", preblock); if preblock_is_fn(preblock) { @@ -410,8 +464,8 @@ fn search_scope_headers(point: Point, scopestart: Point, msrc: Src, searchstr: & // 'if let' can be an expression, so might not be at the start of the stmt } else if let Some(n) = preblock.find("if let") { - let ifletstart = stmtstart + n; - let src = msrc[ifletstart..scopestart+1].to_owned() + "}"; + let ifletstart = stmtstart + n.into(); + let src = msrc[ifletstart.0..scopestart.increment().0].to_owned() + "}"; if txt_matches(search_type, searchstr, &src) { let mut out = matchers::match_if_let(&src, 0, src.len(), searchstr, filepath, search_type, true); @@ -421,7 +475,7 @@ fn search_scope_headers(point: Point, scopestart: Point, msrc: Src, searchstr: & return out.into_iter(); } } else if preblock.starts_with("while let") { - let src = msrc[stmtstart..scopestart+1].to_owned() + "}"; + let src = msrc[stmtstart.0..scopestart.increment().0].to_owned() + "}"; if txt_matches(search_type, searchstr, &src) { let mut out = matchers::match_while_let(&src, 0, src.len(), searchstr, filepath, search_type, true); @@ -431,8 +485,8 @@ fn search_scope_headers(point: Point, scopestart: Point, msrc: Src, searchstr: & return out.into_iter(); } } else if preblock.starts_with("for ") { - let src = msrc[stmtstart..scopestart+1].to_owned() + "}"; - if txt_matches(search_type, searchstr, &msrc[..scopestart]) { + let src = msrc[stmtstart.0..scopestart.increment().0].to_owned() + "}"; + if txt_matches(search_type, searchstr, &msrc[..scopestart.0]) { let mut out = matchers::match_for(&src, 0, src.len(), searchstr, filepath, search_type, true); for m in &mut out { @@ -442,45 +496,45 @@ fn search_scope_headers(point: Point, scopestart: Point, msrc: Src, searchstr: & } } else if let Some(n) = preblock.rfind("match ") { // TODO: this code is crufty. refactor me! - let matchstart = stmtstart + n; - let matchstmt = typeinf::get_first_stmt(msrc.from(matchstart)); + let matchstart = stmtstart + n.into(); + let matchstmt = typeinf::get_first_stmt(msrc.shift_start(matchstart)); // The definition could be in the match LHS arms. Try to find this - let masked_matchstmt = mask_matchstmt(&matchstmt, scopestart + 1 - matchstart); + let masked_matchstmt = mask_matchstmt(&matchstmt, scopestart.increment() - matchstart); debug!("found match stmt, masked is len {} |{}|", masked_matchstmt.len(), masked_matchstmt); // Locate the match arm LHS by finding the => just before point and then backtracking // be sure to be on the right side of the ... => ... arm - let arm = match masked_matchstmt[..point-matchstart].rfind("=>") { + let arm = match masked_matchstmt[..(point - matchstart).0].rfind("=>") { None => // we are in the first arm enum return Vec::new().into_iter(), Some(arm) => { // be sure not to be in the next arm enum - if let Some(next_arm) = masked_matchstmt[arm+2..].find("=>") { - let enum_start = scopes::get_start_of_pattern(&masked_matchstmt, arm+next_arm+1); + if let Some(next_arm) = masked_matchstmt[arm + 2..].find("=>") { + let enum_start = scopes::get_start_of_pattern(&masked_matchstmt, BytePos(arm + next_arm + 1)); if point > matchstart+enum_start { return Vec::new().into_iter(); } } - arm + BytePos(arm) } }; - debug!("PHIL matched arm rhs is |{}|", &masked_matchstmt[arm..]); + debug!("PHIL matched arm rhs is |{}|", &masked_matchstmt[arm.0..]); let lhs_start = scopes::get_start_of_pattern(&msrc, matchstart + arm); - let lhs = &msrc[lhs_start..matchstart + arm]; + let lhs = &msrc[lhs_start.0..(matchstart + arm).0]; // Now create a pretend match expression with just the one match arm in it - let faux_prefix_size = scopestart - matchstart + 1; - let fauxmatchstmt = format!("{}{{{} => () }};", &msrc[matchstart..scopestart], lhs); + let faux_prefix_size = scopestart.increment() - matchstart; + let fauxmatchstmt = format!("{}{{{} => () }};", &msrc[matchstart.0..scopestart.0], lhs); debug!("PHIL arm lhs is |{}|", lhs); - debug!("PHIL arm fauxmatchstmt is |{}|, {}", fauxmatchstmt, faux_prefix_size); + debug!("PHIL arm fauxmatchstmt is |{}|, {:?}", fauxmatchstmt, faux_prefix_size); let mut out = Vec::new(); - for (start,end) in ast::parse_pat_idents(fauxmatchstmt) { - let (start,end) = (lhs_start + start - faux_prefix_size, - lhs_start + end - faux_prefix_size); - let s = &msrc[start..end]; + for pat_range in ast::parse_pat_idents(fauxmatchstmt) { + let (start, end) = (lhs_start + pat_range.start - faux_prefix_size, + lhs_start + pat_range.end - faux_prefix_size); + let s = &msrc[start.0..end.0]; if symbol_matches(search_type, searchstr, s) { out.push(Match { @@ -537,9 +591,9 @@ fn is_fn() { assert!(preblock_is_fn("pub(in foo::bar) fn bar()")); } -fn mask_matchstmt(matchstmt_src: &str, innerscope_start: Point) -> String { - let s = scopes::mask_sub_scopes(&matchstmt_src[innerscope_start..]); - matchstmt_src[..innerscope_start].to_owned() + &s +fn mask_matchstmt(matchstmt_src: &str, innerscope_start: BytePos) -> String { + let s = scopes::mask_sub_scopes(&matchstmt_src[innerscope_start.0..]); + matchstmt_src[..innerscope_start.0].to_owned() + &s } #[test] @@ -552,29 +606,35 @@ fn does_it() { debug!("PHIL res is |{}|",res); } -fn search_fn_args(fnstart: Point, open_brace_pos: Point, msrc: &str, - searchstr: &str, filepath: &Path, - search_type: SearchType, local: bool) -> vec::IntoIter { +fn search_fn_args( + fnstart: BytePos, + open_brace_pos: BytePos, + msrc: &str, + searchstr: &str, + filepath: &Path, + search_type: SearchType, + local: bool +) -> vec::IntoIter { let mut out = Vec::new(); let mut fndecl = String::new(); // wrap in 'impl blah {}' so that methods get parsed correctly too fndecl.push_str("impl blah {"); let impl_header_len = fndecl.len(); - fndecl.push_str(&msrc[fnstart..(open_brace_pos+1)]); + fndecl.push_str(&msrc[fnstart.0..open_brace_pos.increment().0]); fndecl.push_str("}}"); - debug!("search_fn_args: found start of fn!! {} |{}| {}", fnstart, fndecl, searchstr); + debug!("search_fn_args: found start of fn!! {:?} |{}| {}", fnstart, fndecl, searchstr); if txt_matches(search_type, searchstr, &fndecl) { let coords = ast::parse_fn_args(fndecl.clone()); - for (start,end) in coords { - let s = &fndecl[start..end]; + for arg_range in coords { + let s = &fndecl[arg_range.to_range()]; debug!("search_fn_args: arg str is |{}|", s); if symbol_matches(search_type, searchstr, s) { let m = Match { matchstr: s.to_owned(), filepath: filepath.to_path_buf(), - point: fnstart + start - impl_header_len, + point: fnstart + arg_range.start - impl_header_len.into(), coords: None, local: local, mtype: FnArg, @@ -651,8 +711,8 @@ pub fn do_file_search( let m = Match { matchstr: fname[3..].to_owned(), filepath: filepath.to_path_buf(), - point: 0, - coords: Some(Coordinate { line: 1, column: 1 }), + point: BytePos::zero(), + coords: Some(Coordinate::start()), local: false, mtype: Module, contextstr: fname[3..].to_owned(), @@ -671,8 +731,8 @@ pub fn do_file_search( let m = Match { matchstr: fname.to_owned(), filepath: filepath.to_path_buf(), - point: 0, - coords: Some(Coordinate { line: 1, column: 1 }), + point: BytePos::zero(), + coords: Some(Coordinate::start()), local: false, mtype: Module, contextstr: filepath.to_str().unwrap().to_owned(), @@ -690,8 +750,8 @@ pub fn do_file_search( let m = Match { matchstr: fname[..(fname.len()-3)].to_owned(), filepath: fpath_buf.clone(), - point: 0, - coords: Some(Coordinate { line: 1, column: 1 }), + point: BytePos::zero(), + coords: Some(Coordinate::start()), local: false, mtype: Module, contextstr: fpath_buf.to_str().unwrap().to_owned(), @@ -753,37 +813,49 @@ pub fn find_possible_crate_root_modules(currentdir: &Path, session: &Session) -> res } -pub fn search_next_scope(mut startpoint: Point, pathseg: &core::PathSegment, - filepath:&Path, search_type: SearchType, local: bool, - namespace: Namespace, session: &Session, - pending_imports: &PendingImports) -> vec::IntoIter { +pub fn search_next_scope( + mut startpoint: BytePos, + pathseg: &core::PathSegment, + filepath:&Path, + search_type: SearchType, + local: bool, + namespace: Namespace, + session: &Session, + pending_imports: &PendingImports +) -> vec::IntoIter { let filesrc = session.load_file(filepath); - if startpoint != 0 { + if startpoint != BytePos::zero() { // is a scope inside the file. Point should point to the definition // (e.g. mod blah {...}), so the actual scope is past the first open brace. - let src = &filesrc[startpoint..]; + let src = &filesrc[startpoint.0..]; //debug!("search_next_scope src1 |{}|",src); // find the opening brace and skip to it. if let Some(n) = src.find('{') { - startpoint += n + 1; + startpoint += BytePos(n + 1); } } search_scope(startpoint, startpoint, filesrc.as_src(), pathseg, filepath, search_type, local, namespace, session, pending_imports) } -pub fn search_scope(start: Point, point: Point, src: Src, - pathseg: &core::PathSegment, - filepath:&Path, search_type: SearchType, local: bool, - namespace: Namespace, - session: &Session, - pending_imports: &PendingImports) -> vec::IntoIter { +pub fn search_scope( + start: BytePos, + point: BytePos, + src: Src, + pathseg: &core::PathSegment, + filepath:&Path, + search_type: SearchType, + local: bool, + namespace: Namespace, + session: &Session, + pending_imports: &PendingImports +) -> vec::IntoIter { let searchstr = &pathseg.name; let mut out = Vec::new(); - debug!("searching scope {:?} start: {} point: {} '{}' {:?} {:?} local: {}, session: {:?}", + debug!("searching scope {:?} start: {:?} point: {:?} '{}' {:?} {:?} local: {}, session: {:?}", namespace, start, point, searchstr, filepath.display(), search_type, local, session); - let scopesrc = src.from(start); + let scopesrc = src.shift_start(start); let mut skip_next_block = false; let mut delayed_single_imports = Vec::new(); let mut delayed_glob_imports = Vec::new(); @@ -792,14 +864,14 @@ pub fn search_scope(start: Point, point: Point, src: Src, // collect up to point so we can search backwards for let bindings // (these take precidence over local fn declarations etc.. - for (blobstart, blobend) in &mut codeit { + for blob_range in &mut codeit { // (e.g. #[cfg(test)]) if skip_next_block { skip_next_block = false; continue; } - let blob = &scopesrc[blobstart..blobend]; + let blob = &scopesrc[blob_range.to_range()]; // for now skip stuff that's meant for testing. Often the test // module hierarchy is incompatible with the non-test @@ -809,23 +881,28 @@ pub fn search_scope(start: Point, point: Point, src: Src, continue; } - v.push((blobstart,blobend)); + v.push(blob_range); - if blobstart > point { + if blob_range.start > point { break; } } // search backwards from point for let bindings - for &(blobstart, blobend) in v.iter().rev() { - if (start+blobend) >= point { + for &blob_range in v.iter().rev() { + if (start + blob_range.end) >= point { continue; } - for m in matchers::match_let(&src, start+blobstart, - start+blobend, - searchstr, - filepath, search_type, local) { + for m in matchers::match_let( + &src, + start + blob_range.start, + start+blobend, + searchstr, + filepath, + search_type, + local + ) { out.push(m); if let ExactMatch = search_type { return out.into_iter(); @@ -1223,7 +1300,7 @@ pub fn resolve_name(pathseg: &core::PathSegment, filepath: &Path, pos: Point, } // Get the scope corresponding to super:: -pub fn get_super_scope(filepath: &Path, pos: Point, session: &Session, +pub fn get_super_scope(filepath: &Path, pos: BytePos, session: &Session, pending_imports: &PendingImports) -> Option { let msrc = session.load_file_and_mask_comments(filepath); let mut path = scopes::get_local_module_path(msrc.as_src(), pos); diff --git a/src/racer/scopes.rs b/src/racer/scopes.rs index a8ec6e59..44b9f60f 100644 --- a/src/racer/scopes.rs +++ b/src/racer/scopes.rs @@ -1,5 +1,5 @@ use {ast, typeinf, util}; -use core::{Src, CompletionType, Point, SourceByteRange}; +use core::{Src, CompletionType, BytePos, ByteRange}; #[cfg(test)] use core::{self, Coordinate}; @@ -9,11 +9,14 @@ use std::str::from_utf8; use util::{closure_valid_arg_scope, char_at}; use codecleaner::comment_skip_iter_rev; -fn find_close<'a, A>(iter: A, open: u8, close: u8, level_end: u32) -> Option where A: Iterator { +fn find_close<'a, A>(iter: A, open: u8, close: u8, level_end: u32) -> Option +where + A: Iterator +{ let mut levels = 0u32; for (count, &b) in iter.enumerate() { if b == close { - if levels == level_end { return Some(count); } + if levels == level_end { return Some(count.into()); } if levels == 0 { return None; } levels -= 1; } else if b == open { levels += 1; } @@ -21,39 +24,42 @@ fn find_close<'a, A>(iter: A, open: u8, close: u8, level_end: u32) -> Option Point { - find_close(src.as_bytes()[pos..].iter(), b'(', b')', 0) - .map_or(src.len(), |count| pos + count) +pub fn find_closing_paren(src: &str, pos: BytePos) -> BytePos { + find_close(src.as_bytes()[pos.0..].iter(), b'(', b')', 0) + .map_or(src.len().into(), |count| pos + count) } -pub fn find_closure_scope_start(src: Src, point: Point, parentheses_open_pos: Point) -> Option { +pub fn find_closure_scope_start( + src: Src, + point: BytePos, + parentheses_open_pos: BytePos +) -> Option { let masked_src = mask_comments(src.from(point)); - - let closing_paren_pos = find_closing_paren(masked_src.as_str(), 0) + point; - + let closing_paren_pos = find_closing_paren(masked_src.as_str(), 0.into()) + point; let src_between_parent = mask_comments(src.from_to(parentheses_open_pos, closing_paren_pos)); + closure_valid_arg_scope(&src_between_parent).map(|_ |parentheses_open_pos) +} - closure_valid_arg_scope(&src_between_parent).map(|_ |parentheses_open_pos)} - -pub fn scope_start(src: Src, point: Point) -> Point { +pub fn scope_start(src: Src, point: BytePos) -> BytePos { let masked_src = mask_comments(src.to(point)); let mut curly_parent_open_pos = find_close(masked_src.as_bytes().iter().rev(), b'}', b'{', 0) - .map_or(0, |count| point - count); + .map_or(BytePos::zero(), |count| point - count); // We've found a multi-use statement, such as `use foo::{bar, baz};`, so we shouldn't consider // the brace to be the start of the scope. - if curly_parent_open_pos > 0 && masked_src[..curly_parent_open_pos].ends_with("::{") { - trace!("scope_start landed in a use statement for {}; broadening search", point); + if curly_parent_open_pos > BytePos(0) && masked_src[..curly_parent_open_pos.0].ends_with("::{") { + trace!("scope_start landed in a use statement for {:?}; broadening search", point); curly_parent_open_pos = find_close( - mask_comments(src.to(curly_parent_open_pos - 1)).as_bytes().iter().rev(), + mask_comments(src.to(curly_parent_open_pos - 1.into())).as_bytes().iter().rev(), b'}', - b'{', - 0).map_or(0, |count| point - count); + b'{', + 0, + ).map_or(BytePos::zero(), |count| point - count); } let parent_open_pos = find_close(masked_src.as_bytes().iter().rev(), b')', b'(', 0) - .map_or(0, |count| point - count); + .map_or(BytePos::zero(), |count| point - count); if curly_parent_open_pos > parent_open_pos { curly_parent_open_pos @@ -64,89 +70,97 @@ pub fn scope_start(src: Src, point: Point) -> Point { } } -pub fn find_stmt_start(msrc: Src, point: Point) -> Option { +pub fn find_stmt_start(msrc: Src, point: BytePos) -> Option { let scopestart = scope_start(msrc, point); // Iterate the scope to find the start of the statement that surrounds the point. debug!( - "[find_stmt_start] now we are in scope {} ~ {}, {}", + "[find_stmt_start] now we are in scope {:?} ~ {:?}, {}", scopestart, point, - &msrc[scopestart..point + 1] + &msrc[scopestart.0..point.increment().0] ); - msrc.from(scopestart).iter_stmts() - .find(|&(start, end)| scopestart + start < point && point < scopestart + end) - .map(|(start, _)| scopestart + start) + msrc.shift_start(scopestart).iter_stmts() + .find(|&range| scopestart + range.start < point && point < scopestart + range.end) + .map(|range| scopestart + range.start) } /// Finds a statement start or panics. -pub fn expect_stmt_start(msrc: Src, point: Point) -> Point { +pub fn expect_stmt_start(msrc: Src, point: BytePos) -> BytePos { find_stmt_start(msrc, point).expect("Statement does not have a beginning") } /// Finds the start of a `let` statement; includes handling of struct pattern matches in the /// statement. -pub fn find_let_start(msrc: Src, point: Point) -> Option { +pub fn find_let_start(msrc: Src, point: BytePos) -> Option { let mut scopestart = scope_start(msrc, point); let mut let_start = None; // To avoid infinite loops, we cap the number of times we'll // expand the search in an attempt to find statements. + // TODO: it's too hacky, and, 1..6 is appropriate? for step in 1..6 { let_start = msrc.from(scopestart).iter_stmts() - .find(|&(_, end)| scopestart + end > point); + .find(|&range| scopestart + range.end > point); - if let Some((ref start, ref end)) = let_start { + if let Some(ref range) = let_start { // Check if we've actually reached the start of the "let" stmt. - let stmt = &msrc.src.code[(scopestart+start)..(scopestart+end)]; + let stmt = &msrc.src.code[range.shift(scopestart).to_range()]; if stmt.starts_with("let") { break; } } - debug!("find_let_start failed to find start on attempt {}: Restarting search from {} ({:?})", + debug!( + "find_let_start failed to find start on attempt {}: Restarting search from {:?} ({:?})", step, - scopestart - 1, - msrc.src.point_to_coords(scopestart - 1)); - scopestart = scope_start(msrc, scopestart - 1); + scopestart.decrement(), + msrc.src.point_to_coords(scopestart.decrement()), + ); + scopestart = scope_start(msrc, scopestart.decrement()); } - let_start.map(|(start, _)| scopestart + start) + let_start.map(|range| scopestart + range.start) } -pub fn get_local_module_path(msrc: Src, point: Point) -> Vec { +pub fn get_local_module_path(msrc: Src, point: BytePos) -> Vec { let mut v = Vec::new(); get_local_module_path_(msrc, point, &mut v); v } -fn get_local_module_path_(msrc: Src, point: Point, out: &mut Vec) { - for (start, end) in msrc.iter_stmts() { - if start < point && end > point { - let blob = msrc.from_to(start, end); +fn get_local_module_path_(msrc: Src, point: BytePos, out: &mut Vec) { + for range in msrc.iter_stmts() { + // FIX_BEFORE_PR: changed from original + if range.contains(point) { + let blob = msrc.shift_range(range); if blob.starts_with("pub mod ") || blob.starts_with("mod ") { let p = typeinf::generate_skeleton_for_parsing(&blob); if let Some(name) = ast::parse_mod(p).name { out.push(name); let newstart = blob.find('{').unwrap() + 1; - get_local_module_path_(blob.from(newstart), - point - start - newstart, out); + get_local_module_path_( + blob.shift_start(newstart.into()), + point - range.start - newstart.into(), + out, + ); } } } } } -pub fn get_module_file_from_path(msrc: Src, point: Point, parentdir: &Path) -> Option { +pub fn get_module_file_from_path(msrc: Src, point: BytePos, parentdir: &Path) -> Option { let mut iter = msrc.iter_stmts(); - while let Some((start, end)) = iter.next() { - let blob = msrc.from_to(start, end); + while let Some(range) = iter.next() { + let blob = msrc.shift_range(range); + let start = range.start; if blob.starts_with("#[path ") { - if let Some((_,modend)) = iter.next(){ + if let Some(ByteRange { start: _, end: modend }) = iter.next() { + // FIX_BEFORE_PR: not same as get_local_module_path if start < point && modend > point { - let pathstart = blob.find('"').unwrap() + 1; + let pathstart = blob.find('"')? + 1; let pathend = blob[pathstart..].find('"').unwrap(); - let path = &blob[pathstart..pathstart+pathend]; - + let path = &blob[pathstart..pathstart + pathend]; debug!("found a path attribute, path = |{}|", path); let filepath = parentdir.join(path); if filepath.exists() { @@ -159,22 +173,24 @@ pub fn get_module_file_from_path(msrc: Src, point: Point, parentdir: &Path) -> O None } -pub fn find_impl_start(msrc: Src, point: Point, scopestart: Point) -> Option { - let len = point-scopestart; - match msrc.from(scopestart).iter_stmts().find(|&(_, end)| end > len) { - Some((start, _)) => { - let blob = msrc.from(scopestart + start); +pub fn find_impl_start(msrc: Src, point: BytePos, scopestart: BytePos) -> Option { + let len = point - scopestart; + msrc + .shift_start(scopestart) + .iter_stmts() + .find(|range| range.end > len) + .and_then(|range| { + let blob = msrc.shift_start(scopestart + range.start); // TODO:: the following is a bit weak at matching traits. make this better if blob.starts_with("impl") || blob.starts_with("trait") || blob.starts_with("pub trait") { - Some(scopestart + start) + Some(scopestart + range.start) } else { - let newstart = blob.find('{').unwrap() + 1; - find_impl_start(msrc, point, scopestart+start+newstart) + let newstart = blob.find('{').expect("[find_impl_start] { was not found.") + 1; + find_impl_start(msrc, point, scopestart + range.start + newstart.into()) } - }, - None => None - } + }) } + #[test] fn finds_subnested_module() { use core; @@ -195,6 +211,7 @@ fn finds_subnested_module() { assert_eq!("foo", &v[0][..]); } +// TODO: This function can't handle use_nested_groups pub fn split_into_context_and_completion(s: &str) -> (&str, &str, CompletionType) { match s.char_indices().rev().find(|&(_, c)| !util::is_ident_char(c)) { Some((i,c)) => { @@ -208,21 +225,19 @@ pub fn split_into_context_and_completion(s: &str) -> (&str, &str, CompletionType } } -pub fn get_line(src: &str, point: Point) -> Point { - let mut i = point; - for &b in src.as_bytes()[..point].iter().rev() { - i-=1; - if b == b'\n' { - return i+1; - } - } - 0 +pub fn get_line(src: &str, point: BytePos) -> BytePos { + src.as_bytes()[..point.0] + .iter() + .enumerate() + .rev() + .find(|(i, byte)| **byte == b'\n') + .map(|t| t.0.into()) + .unwrap_or_else(BytePos::zero) } /// search in reverse for the start of the current expression /// allow . and :: to be surrounded by white chars to enable multi line call chains -pub fn get_start_of_search_expr(src: &str, point: Point) -> Point { - +pub fn get_start_of_search_expr(src: &str, point: BytePos) -> BytePos { enum State { /// In parentheses; the value inside identifies depth. Levels(usize), @@ -232,10 +247,10 @@ pub fn get_start_of_search_expr(src: &str, point: Point) -> Point { MustEndsWithDot(usize), StartsWithCol(usize), None, - Result(Point), + Result(usize), } let mut ws_ok = State::None; - for (i, c) in src.as_bytes()[..point].iter().enumerate().rev() { + for (i, c) in src.as_bytes()[..point.0].iter().enumerate().rev() { ws_ok = match (*c,ws_ok) { (b'(', State::None) => State::Result(i+1), (b'(', State::Levels(1)) => State::None, @@ -266,13 +281,13 @@ pub fn get_start_of_search_expr(src: &str, point: Point) -> Point { ( _ , State::Result(_)) => unreachable!() , }; if let State::Result(index) = ws_ok { - return index; + return index.into(); } } - 0 + BytePos::zero() } -pub fn get_start_of_pattern(src: &str, point: Point) -> Point { +pub fn get_start_of_pattern(src: &str, point: BytePos) -> BytePos { let mut levels = 0u32; for (c, i) in comment_skip_iter_rev(src, point) { match c { @@ -290,7 +305,7 @@ pub fn get_start_of_pattern(src: &str, point: Point) -> Point { } } } - 0 + BytePos::zero() } #[test] @@ -308,7 +323,7 @@ fn get_start_of_pattern_with_block_comments() { assert_eq!(6, get_start_of_pattern("break, Some(b) /* if expr is Some */ => {", 36)); } -pub fn expand_search_expr(msrc: &str, point: Point) -> SourceByteRange { +pub fn expand_search_expr(msrc: &str, point: BytePos) -> ByteRange { let start = get_start_of_search_expr(msrc, point); (start, util::find_ident_end(msrc, point)) } @@ -386,16 +401,16 @@ pub fn mask_comments(src: Src) -> String { let mut result = String::with_capacity(src.len()); let buf_byte = &[b' '; 128]; let buffer = from_utf8(buf_byte).unwrap(); - let mut prev: usize = 0; - for (start, end) in src.chunk_indices() { - fill_gaps(buffer, &mut result, start, prev); - result.push_str(&src[start..end]); - prev = end; + let mut prev = BytePos::zero(); + for range in src.chunk_indices() { + fill_gaps(buffer, &mut result, range.start.0, prev.0); + result.push_str(&src[range.to_range()]); + prev = range.end; } // Fill up if the comment was at the end - if src.len() > prev { - fill_gaps(buffer, &mut result, src.len(), prev); + if src.len() > prev.0 { + fill_gaps(buffer, &mut result, src.len(), prev.0); } result @@ -448,7 +463,7 @@ pub fn mask_sub_scopes(src: &str) -> String { pub fn end_of_next_scope(src: &str) -> &str { match find_close(src.as_bytes().iter(), b'{', b'}', 1) { - Some(count) => &src[..count+1], + Some(count) => &src[..count.0 + 1], None => "" } } diff --git a/src/racer/typeinf.rs b/src/racer/typeinf.rs index b1d0b3e0..f2ff3fdd 100644 --- a/src/racer/typeinf.rs +++ b/src/racer/typeinf.rs @@ -1,6 +1,6 @@ // Type inference -use core::{Match, Src, Scope, Session, SessionExt, Point}; +use core::{Match, Src, Scope, Session, SessionExt, BytePos}; use nameres::resolve_path_with_str; use core::Namespace; use core; @@ -11,9 +11,12 @@ use core::SearchType::ExactMatch; use util::{self, txt_matches}; use std::path::Path; -fn find_start_of_function_body(src: &str) -> Point { +// FIX_BEFORE_PR: Option +fn find_start_of_function_body(src: &str) -> BytePos { // TODO: this should ignore anything inside parens so as to skip the arg list - src.find('{').expect("Function body should have a beginning") + src.find('{') + .map(|u| BytePos::from(u)) + .expect("Function body should have a beginning") } // Removes the body of the statement (anything in the braces {...}), leaving just @@ -22,7 +25,7 @@ fn find_start_of_function_body(src: &str) -> Point { pub fn generate_skeleton_for_parsing(src: &str) -> String { let mut s = String::new(); let n = find_start_of_function_body(src); - s.push_str(&src[..n+1]); + s.push_str(&src[..n.0 + 1]); s.push_str("};"); s } @@ -66,10 +69,11 @@ pub fn first_param_is_self(blob: &str) -> bool { Some(..) => 0, }; if let Some(start) = blob[skip_generic..].find('(') { - let end = scopes::find_closing_paren(blob, start + 1); - let is_self = txt_matches(ExactMatch, "self", &blob[(start + 1)..end]); + let start = BytePos::from(start).increment(); + let end = scopes::find_closing_paren(blob, start); + let is_self = txt_matches(ExactMatch, "self", &blob[start.0..end.0]); trace!("searching fn args for self: |{}| {}", - &blob[(start + 1)..end], + &blob[start.0..end.0], is_self); return is_self; } @@ -90,9 +94,15 @@ fn get_type_of_self_arg(m: &Match, msrc: Src, session: &Session) -> Option Option { - scopes::find_impl_start(msrc, point, 0).and_then(|start| { - let decl = generate_skeleton_for_parsing(&msrc.from(start)); +pub fn get_type_of_self( + point: BytePos, + filepath: &Path, + local: bool, + msrc: Src, + session: &Session +) -> Option { + scopes::find_impl_start(msrc, point, BytePos::zero()).and_then(|start| { + let decl = generate_skeleton_for_parsing(&msrc.shift_start(start)); debug!("get_type_of_self_arg impl skeleton |{}|", decl); if decl.starts_with("impl") { @@ -112,7 +122,7 @@ pub fn get_type_of_self(point: Point, filepath: &Path, local: bool, msrc: Src, s coords: None, local: local, mtype: core::MatchType::Trait, - contextstr: matchers::first_line(&msrc[start..]), + contextstr: matchers::first_line(&msrc[start.0..]), generic_args: Vec::new(), generic_types: Vec::new(), docs: String::new(), @@ -127,14 +137,14 @@ fn is_closure(src: &str) -> Option { Some(s == "|") } -fn find_start_of_closure_body(src: &str) -> Option { +fn find_start_of_closure_body(src: &str) -> Option { let mut cnt = 0; for (i, c) in src.chars().enumerate() { if c == '|' { cnt += 1; } if cnt == 2 { - return Some(i + 1); + return Some(BytePos::from(i).increment()); } } warn!( @@ -159,15 +169,15 @@ fn get_type_of_fnarg(m: &Match, msrc: Src, session: &Session) -> Option Option Option Option { // ASSUMPTION: this is being called on a let decl let point = scopes::find_let_start(msrc, m.point).expect("`let` should have a beginning"); - let src = msrc.from(point); + let src = msrc.shift_start(point); - if let Some((start, end)) = src.iter_stmts().next() { - let blob = &src[start..end]; + if let Some(range) = src.iter_stmts().next() { + let blob = &src[range.to_range()]; debug!("get_type_of_let_expr calling parse_let |{}|", blob); - let pos = m.point - point - start; + let pos = m.point - point - range.start; let scope = Scope { filepath: m.filepath.clone(), point }; ast::get_let_type(blob.to_owned(), pos, scope, session) } else { @@ -200,18 +210,18 @@ fn get_type_of_let_expr(m: &Match, msrc: Src, session: &Session) -> Option Option { - // ASSUMPTION: this is being called on an if let or while let decl let stmtstart = scopes::find_stmt_start(msrc, m.point).expect("`let` should have a beginning"); - let stmt = msrc.from(stmtstart); - let point = stmt.find(prefix).expect("`prefix` should appear in statement"); - let src = core::new_source(generate_skeleton_for_parsing(&stmt[point..])); + let stmt = msrc.shift_start(stmtstart); + let point: BytePos = stmt.find(prefix).expect("`prefix` should appear in statement").into(); + let src = core::new_source(generate_skeleton_for_parsing(&stmt[point.0..])); - if let Some((start, end)) = src.as_src().iter_stmts().next() { - let blob = &src[start..end]; + if let Some(range) = src.as_src().iter_stmts().next() { + let blob = &src[range.to_range()]; debug!("get_type_of_let_block_expr calling get_let_type |{}|", blob); - let pos = m.point - stmtstart - point - start; + let pos = m.point - stmtstart - point - range.start; let scope = Scope{ filepath: m.filepath.clone(), point: stmtstart }; ast::get_let_type(blob.to_owned(), pos, scope, session) } else { @@ -221,7 +231,7 @@ fn get_type_of_let_block_expr(m: &Match, msrc: Src, session: &Session, prefix: & fn get_type_of_for_expr(m: &Match, msrc: Src, session: &Session) -> Option { let stmtstart = scopes::expect_stmt_start(msrc, m.point); - let stmt = msrc.from(stmtstart); + let stmt = msrc.shift_start(stmtstart); let forpos = stmt.find("for ").expect("`for` should appear in for .. in loop"); let inpos = stmt.find(" in ").expect("`in` should appear in for .. in loop"); // XXX: this need not be the correct brace, see generate_skeleton_for_parsing @@ -243,11 +253,18 @@ fn get_type_of_for_expr(m: &Match, msrc: Src, session: &Session) -> Option Src { match src.iter_stmts().next() { - Some((from, to)) => src.from_to(from, to), + Some(range) => src.shift_range(range), None => src } } @@ -347,32 +364,28 @@ pub fn get_type_of_match(m: Match, msrc: Src, session: &Session) -> Option (match $e { Some(e) => e, None => return None }) -} - pub fn get_type_from_match_arm(m: &Match, msrc: Src, session: &Session) -> Option { // We construct a faux match stmt and then parse it. This is because the // match stmt may be incomplete (half written) in the real code // skip to end of match arm pattern so we can search backwards - let arm = otry!(msrc[m.point..].find("=>")) + m.point; + let arm = msrc[m.point.0..].find("=>")?.into() + m.point; let scopestart = scopes::scope_start(msrc, arm); - let stmtstart = otry!(scopes::find_stmt_start(msrc, scopestart-1)); - debug!("PHIL preblock is {} {}", stmtstart, scopestart); - let preblock = &msrc[stmtstart..scopestart]; - let matchstart = otry!(preblock.rfind("match ")) + stmtstart; + let stmtstart = scopes::find_stmt_start(msrc, scopestart.decrement())?; + debug!("PHIL preblock is {:?} {:?}", stmtstart, scopestart); + let preblock = &msrc[stmtstart.0..scopestart.0]; + let matchstart = stmtstart + preblock.rfind("match ")?.into(); let lhs_start = scopes::get_start_of_pattern(&msrc, arm); - let lhs = &msrc[lhs_start..arm]; + let lhs = &msrc[lhs_start.0..arm.0]; // construct faux match statement and recreate point - let mut fauxmatchstmt = msrc[matchstart..scopestart].to_owned(); - let faux_prefix_size = fauxmatchstmt.len(); + let mut fauxmatchstmt = msrc[matchstart.0..scopestart.0].to_owned(); + let faux_prefix_size = BytePos::from(fauxmatchstmt.len()); fauxmatchstmt = fauxmatchstmt + lhs + " => () };"; let faux_point = faux_prefix_size + (m.point - lhs_start); - debug!("fauxmatchstmt for parsing is pt:{} src:|{}|", faux_point, fauxmatchstmt); + debug!("fauxmatchstmt for parsing is pt:{:?} src:|{}|", faux_point, fauxmatchstmt); ast::get_match_arm_type(fauxmatchstmt, faux_point, // scope is used to locate expression, so send @@ -387,18 +400,18 @@ pub fn get_function_declaration(fnmatch: &Match, session: &Session) -> String { let src = session.load_file(&fnmatch.filepath); let start = scopes::expect_stmt_start(src.as_src(), fnmatch.point); let def_end: &[_] = &['{', ';']; - let end = src[start..].find(def_end).expect("Definition should have an end (`{` or `;`)"); - src[start..end+start].to_owned() + let end = src[start.0..].find(def_end).expect("Definition should have an end (`{` or `;`)"); + src[start.0..start.0 + end].to_owned() } pub fn get_return_type_of_function(fnmatch: &Match, contextm: &Match, session: &Session) -> Option { let src = session.load_file(&fnmatch.filepath); let point = scopes::expect_stmt_start(src.as_src(), fnmatch.point); - let out = src[point..].find(|c| {c == '{' || c == ';'}).and_then(|n| { + let out = src[point.0..].find(|c| {c == '{' || c == ';'}).and_then(|n| { // wrap in "impl blah { }" so that methods get parsed correctly too let mut decl = String::new(); decl.push_str("impl blah {"); - decl.push_str(&src[point..(point+n+1)]); + decl.push_str(&src[point.0..point.0 + n + 1]); if decl.ends_with(';') { decl.pop(); decl.push_str("{}}"); diff --git a/src/racer/util.rs b/src/racer/util.rs index 208cac30..7c2fe10a 100644 --- a/src/racer/util.rs +++ b/src/racer/util.rs @@ -3,7 +3,7 @@ use std::{cmp, error, fmt, path}; use std::rc::Rc; use std::{collections::hash_map::DefaultHasher, hash::{Hash, Hasher}}; -use core::{IndexedSource, Session, SessionExt, Location, LocationExt, Point}; +use core::{IndexedSource, Session, SessionExt, Location, LocationExt, BytePos}; use core::SearchType::{self, ExactMatch, StartsWith}; #[cfg(unix)] @@ -26,6 +26,7 @@ pub fn is_ident_char(c: char) -> bool { /// Searches for `needle` as a standalone identifier in `haystack`. To be considered a match, /// the `needle` must occur either at the beginning of `haystack` or after a non-identifier /// character. +// TODO: this function should returns the position of found keyword pub fn txt_matches(stype: SearchType, needle: &str, haystack: &str) -> bool { match stype { ExactMatch => { @@ -182,8 +183,8 @@ pub fn expand_ident( // TODO: Would this better be an assertion ? Why are out-of-bound values getting here ? // They are coming from the command-line, question is, if they should be handled beforehand // clamp pos into allowed range - let pos = cmp::min(s.len(), pos); - let sb = &s[..pos]; + let pos = cmp::min(s.len().into(), pos); + let sb = &s[..pos.0]; let mut start = pos; // backtrack to find start of word @@ -191,7 +192,7 @@ pub fn expand_ident( if !is_ident_char(c) { break; } - start = i; + start = i.into(); } (start, pos) @@ -206,33 +207,33 @@ pub fn expand_ident( pub struct ExpandedIdent { src: Rc, - start: Point, - pos: Point, + start: BytePos, + pos: BytePos, } impl ExpandedIdent { pub fn ident(&self) -> &str { - &self.src.code[self.start..self.pos] + &self.src.code[self.start.0..self.pos.0] } - pub fn start(&self) -> Point { + pub fn start(&self) -> BytePos { self.start } - pub fn pos(&self) -> Point { + pub fn pos(&self) -> BytePos { self.pos } } -pub fn find_ident_end(s: &str, pos: Point) -> Point { +pub fn find_ident_end(s: &str, pos: BytePos) -> BytePos { // find end of word - let sa = &s[pos..]; + let sa = &s[pos.0..]; for (i, c) in sa.char_indices() { if !is_ident_char(c) { - return pos + i; + return pos + i.into(); } } - s.len() + s.len().into() } #[test]