diff options
-rw-r--r-- | crates/base_db/src/lib.rs | 3 | ||||
-rw-r--r-- | crates/ide/src/doc_links.rs | 63 | ||||
-rw-r--r-- | crates/ide/src/syntax_highlighting/inject.rs | 32 | ||||
-rw-r--r-- | crates/profile/src/hprof.rs | 87 | ||||
-rw-r--r-- | crates/profile/src/lib.rs | 2 | ||||
-rw-r--r-- | docs/user/manual.adoc | 19 |
6 files changed, 119 insertions, 87 deletions
diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs index 5f77a0b1f..980a0ed98 100644 --- a/crates/base_db/src/lib.rs +++ b/crates/base_db/src/lib.rs | |||
@@ -59,6 +59,8 @@ pub trait CheckCanceled { | |||
59 | Self: Sized + panic::RefUnwindSafe, | 59 | Self: Sized + panic::RefUnwindSafe, |
60 | F: FnOnce(&Self) -> T + panic::UnwindSafe, | 60 | F: FnOnce(&Self) -> T + panic::UnwindSafe, |
61 | { | 61 | { |
62 | // Uncomment to debug missing cancellations. | ||
63 | // let _span = profile::heartbeat_span(); | ||
62 | panic::catch_unwind(|| f(self)).map_err(|err| match err.downcast::<Canceled>() { | 64 | panic::catch_unwind(|| f(self)).map_err(|err| match err.downcast::<Canceled>() { |
63 | Ok(canceled) => *canceled, | 65 | Ok(canceled) => *canceled, |
64 | Err(payload) => panic::resume_unwind(payload), | 66 | Err(payload) => panic::resume_unwind(payload), |
@@ -68,6 +70,7 @@ pub trait CheckCanceled { | |||
68 | 70 | ||
69 | impl<T: salsa::Database> CheckCanceled for T { | 71 | impl<T: salsa::Database> CheckCanceled for T { |
70 | fn check_canceled(&self) { | 72 | fn check_canceled(&self) { |
73 | // profile::heartbeat(); | ||
71 | if self.salsa_runtime().is_current_revision_canceled() { | 74 | if self.salsa_runtime().is_current_revision_canceled() { |
72 | Canceled::throw() | 75 | Canceled::throw() |
73 | } | 76 | } |
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index 9301cdeff..99276168f 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs | |||
@@ -98,6 +98,29 @@ pub(crate) fn remove_links(markdown: &str) -> String { | |||
98 | out | 98 | out |
99 | } | 99 | } |
100 | 100 | ||
101 | /// Retrieve a link to documentation for the given symbol. | ||
102 | pub(crate) fn external_docs( | ||
103 | db: &RootDatabase, | ||
104 | position: &FilePosition, | ||
105 | ) -> Option<DocumentationLink> { | ||
106 | let sema = Semantics::new(db); | ||
107 | let file = sema.parse(position.file_id).syntax().clone(); | ||
108 | let token = pick_best(file.token_at_offset(position.offset))?; | ||
109 | let token = sema.descend_into_macros(token); | ||
110 | |||
111 | let node = token.parent()?; | ||
112 | let definition = match_ast! { | ||
113 | match node { | ||
114 | ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)), | ||
115 | ast::Name(name) => NameClass::classify(&sema, &name).map(|d| d.referenced_or_defined(sema.db)), | ||
116 | _ => None, | ||
117 | } | ||
118 | }; | ||
119 | |||
120 | get_doc_link(db, definition?) | ||
121 | } | ||
122 | |||
123 | /// Extracts all links from a given markdown text. | ||
101 | pub(crate) fn extract_definitions_from_markdown( | 124 | pub(crate) fn extract_definitions_from_markdown( |
102 | markdown: &str, | 125 | markdown: &str, |
103 | ) -> Vec<(Range<usize>, String, Option<hir::Namespace>)> { | 126 | ) -> Vec<(Range<usize>, String, Option<hir::Namespace>)> { |
@@ -178,15 +201,15 @@ pub(crate) fn resolve_doc_path_for_def( | |||
178 | ) -> Option<hir::ModuleDef> { | 201 | ) -> Option<hir::ModuleDef> { |
179 | match def { | 202 | match def { |
180 | Definition::ModuleDef(def) => match def { | 203 | Definition::ModuleDef(def) => match def { |
181 | ModuleDef::Module(it) => it.resolve_doc_path(db, &link, ns), | 204 | hir::ModuleDef::Module(it) => it.resolve_doc_path(db, &link, ns), |
182 | ModuleDef::Function(it) => it.resolve_doc_path(db, &link, ns), | 205 | hir::ModuleDef::Function(it) => it.resolve_doc_path(db, &link, ns), |
183 | ModuleDef::Adt(it) => it.resolve_doc_path(db, &link, ns), | 206 | hir::ModuleDef::Adt(it) => it.resolve_doc_path(db, &link, ns), |
184 | ModuleDef::Variant(it) => it.resolve_doc_path(db, &link, ns), | 207 | hir::ModuleDef::Variant(it) => it.resolve_doc_path(db, &link, ns), |
185 | ModuleDef::Const(it) => it.resolve_doc_path(db, &link, ns), | 208 | hir::ModuleDef::Const(it) => it.resolve_doc_path(db, &link, ns), |
186 | ModuleDef::Static(it) => it.resolve_doc_path(db, &link, ns), | 209 | hir::ModuleDef::Static(it) => it.resolve_doc_path(db, &link, ns), |
187 | ModuleDef::Trait(it) => it.resolve_doc_path(db, &link, ns), | 210 | hir::ModuleDef::Trait(it) => it.resolve_doc_path(db, &link, ns), |
188 | ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, &link, ns), | 211 | hir::ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, &link, ns), |
189 | ModuleDef::BuiltinType(_) => None, | 212 | hir::ModuleDef::BuiltinType(_) => None, |
190 | }, | 213 | }, |
191 | Definition::Macro(it) => it.resolve_doc_path(db, &link, ns), | 214 | Definition::Macro(it) => it.resolve_doc_path(db, &link, ns), |
192 | Definition::Field(it) => it.resolve_doc_path(db, &link, ns), | 215 | Definition::Field(it) => it.resolve_doc_path(db, &link, ns), |
@@ -328,28 +351,6 @@ fn rewrite_url_link(db: &RootDatabase, def: ModuleDef, target: &str) -> Option<S | |||
328 | .map(|url| url.into_string()) | 351 | .map(|url| url.into_string()) |
329 | } | 352 | } |
330 | 353 | ||
331 | /// Retrieve a link to documentation for the given symbol. | ||
332 | pub(crate) fn external_docs( | ||
333 | db: &RootDatabase, | ||
334 | position: &FilePosition, | ||
335 | ) -> Option<DocumentationLink> { | ||
336 | let sema = Semantics::new(db); | ||
337 | let file = sema.parse(position.file_id).syntax().clone(); | ||
338 | let token = pick_best(file.token_at_offset(position.offset))?; | ||
339 | let token = sema.descend_into_macros(token); | ||
340 | |||
341 | let node = token.parent()?; | ||
342 | let definition = match_ast! { | ||
343 | match node { | ||
344 | ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)), | ||
345 | ast::Name(name) => NameClass::classify(&sema, &name).map(|d| d.referenced_or_defined(sema.db)), | ||
346 | _ => None, | ||
347 | } | ||
348 | }; | ||
349 | |||
350 | get_doc_link(db, definition?) | ||
351 | } | ||
352 | |||
353 | /// Rewrites a markdown document, applying 'callback' to each link. | 354 | /// Rewrites a markdown document, applying 'callback' to each link. |
354 | fn map_links<'e>( | 355 | fn map_links<'e>( |
355 | events: impl Iterator<Item = Event<'e>>, | 356 | events: impl Iterator<Item = Event<'e>>, |
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 963c3fb59..b62d43256 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs | |||
@@ -11,7 +11,8 @@ use syntax::{ | |||
11 | }; | 11 | }; |
12 | 12 | ||
13 | use crate::{ | 13 | use crate::{ |
14 | doc_links::extract_definitions_from_markdown, Analysis, HlMod, HlRange, HlTag, RootDatabase, | 14 | doc_links::{extract_definitions_from_markdown, resolve_doc_path_for_def}, |
15 | Analysis, HlMod, HlRange, HlTag, RootDatabase, | ||
15 | }; | 16 | }; |
16 | 17 | ||
17 | use super::{highlights::Highlights, injector::Injector}; | 18 | use super::{highlights::Highlights, injector::Injector}; |
@@ -190,7 +191,7 @@ pub(super) fn doc_comment( | |||
190 | extract_definitions_from_markdown(line) | 191 | extract_definitions_from_markdown(line) |
191 | .into_iter() | 192 | .into_iter() |
192 | .filter_map(|(range, link, ns)| { | 193 | .filter_map(|(range, link, ns)| { |
193 | Some(range).zip(validate_intra_doc_link(sema.db, &def, &link, ns)) | 194 | Some(range).zip(resolve_doc_path_for_def(sema.db, def, &link, ns)) |
194 | }) | 195 | }) |
195 | .map(|(Range { start, end }, def)| { | 196 | .map(|(Range { start, end }, def)| { |
196 | ( | 197 | ( |
@@ -283,33 +284,6 @@ fn find_doc_string_in_attr(attr: &hir::Attr, it: &ast::Attr) -> Option<ast::Stri | |||
283 | } | 284 | } |
284 | } | 285 | } |
285 | 286 | ||
286 | fn validate_intra_doc_link( | ||
287 | db: &RootDatabase, | ||
288 | def: &Definition, | ||
289 | link: &str, | ||
290 | ns: Option<hir::Namespace>, | ||
291 | ) -> Option<hir::ModuleDef> { | ||
292 | match def { | ||
293 | Definition::ModuleDef(def) => match def { | ||
294 | hir::ModuleDef::Module(it) => it.resolve_doc_path(db, &link, ns), | ||
295 | hir::ModuleDef::Function(it) => it.resolve_doc_path(db, &link, ns), | ||
296 | hir::ModuleDef::Adt(it) => it.resolve_doc_path(db, &link, ns), | ||
297 | hir::ModuleDef::Variant(it) => it.resolve_doc_path(db, &link, ns), | ||
298 | hir::ModuleDef::Const(it) => it.resolve_doc_path(db, &link, ns), | ||
299 | hir::ModuleDef::Static(it) => it.resolve_doc_path(db, &link, ns), | ||
300 | hir::ModuleDef::Trait(it) => it.resolve_doc_path(db, &link, ns), | ||
301 | hir::ModuleDef::TypeAlias(it) => it.resolve_doc_path(db, &link, ns), | ||
302 | hir::ModuleDef::BuiltinType(_) => None, | ||
303 | }, | ||
304 | Definition::Macro(it) => it.resolve_doc_path(db, &link, ns), | ||
305 | Definition::Field(it) => it.resolve_doc_path(db, &link, ns), | ||
306 | Definition::SelfType(_) | ||
307 | | Definition::Local(_) | ||
308 | | Definition::GenericParam(_) | ||
309 | | Definition::Label(_) => None, | ||
310 | } | ||
311 | } | ||
312 | |||
313 | fn module_def_to_hl_tag(def: hir::ModuleDef) -> HlTag { | 287 | fn module_def_to_hl_tag(def: hir::ModuleDef) -> HlTag { |
314 | let symbol = match def { | 288 | let symbol = match def { |
315 | hir::ModuleDef::Module(_) => SymbolKind::Module, | 289 | hir::ModuleDef::Module(_) => SymbolKind::Module, |
diff --git a/crates/profile/src/hprof.rs b/crates/profile/src/hprof.rs index 014e906e6..5fdb37206 100644 --- a/crates/profile/src/hprof.rs +++ b/crates/profile/src/hprof.rs | |||
@@ -69,6 +69,20 @@ pub fn span(label: Label) -> ProfileSpan { | |||
69 | } | 69 | } |
70 | } | 70 | } |
71 | 71 | ||
72 | #[inline] | ||
73 | pub fn heartbeat_span() -> HeartbeatSpan { | ||
74 | let enabled = PROFILING_ENABLED.load(Ordering::Relaxed); | ||
75 | HeartbeatSpan::new(enabled) | ||
76 | } | ||
77 | |||
78 | #[inline] | ||
79 | pub fn heartbeat() { | ||
80 | let enabled = PROFILING_ENABLED.load(Ordering::Relaxed); | ||
81 | if enabled { | ||
82 | with_profile_stack(|it| it.heartbeat(1)); | ||
83 | } | ||
84 | } | ||
85 | |||
72 | pub struct ProfileSpan(Option<ProfilerImpl>); | 86 | pub struct ProfileSpan(Option<ProfilerImpl>); |
73 | 87 | ||
74 | struct ProfilerImpl { | 88 | struct ProfilerImpl { |
@@ -92,6 +106,28 @@ impl Drop for ProfilerImpl { | |||
92 | } | 106 | } |
93 | } | 107 | } |
94 | 108 | ||
109 | pub struct HeartbeatSpan { | ||
110 | enabled: bool, | ||
111 | } | ||
112 | |||
113 | impl HeartbeatSpan { | ||
114 | #[inline] | ||
115 | pub fn new(enabled: bool) -> Self { | ||
116 | if enabled { | ||
117 | with_profile_stack(|it| it.heartbeats(true)) | ||
118 | } | ||
119 | Self { enabled } | ||
120 | } | ||
121 | } | ||
122 | |||
123 | impl Drop for HeartbeatSpan { | ||
124 | fn drop(&mut self) { | ||
125 | if self.enabled { | ||
126 | with_profile_stack(|it| it.heartbeats(false)) | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | |||
95 | static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false); | 131 | static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false); |
96 | static FILTER: Lazy<RwLock<Filter>> = Lazy::new(Default::default); | 132 | static FILTER: Lazy<RwLock<Filter>> = Lazy::new(Default::default); |
97 | 133 | ||
@@ -105,6 +141,7 @@ struct Filter { | |||
105 | depth: usize, | 141 | depth: usize, |
106 | allowed: HashSet<String>, | 142 | allowed: HashSet<String>, |
107 | longer_than: Duration, | 143 | longer_than: Duration, |
144 | heartbeat_longer_than: Duration, | ||
108 | version: usize, | 145 | version: usize, |
109 | } | 146 | } |
110 | 147 | ||
@@ -121,6 +158,7 @@ impl Filter { | |||
121 | } else { | 158 | } else { |
122 | Duration::new(0, 0) | 159 | Duration::new(0, 0) |
123 | }; | 160 | }; |
161 | let heartbeat_longer_than = longer_than; | ||
124 | 162 | ||
125 | let depth = if let Some(idx) = spec.rfind('@') { | 163 | let depth = if let Some(idx) = spec.rfind('@') { |
126 | let depth: usize = spec[idx + 1..].parse().expect("invalid profile depth"); | 164 | let depth: usize = spec[idx + 1..].parse().expect("invalid profile depth"); |
@@ -131,7 +169,7 @@ impl Filter { | |||
131 | }; | 169 | }; |
132 | let allowed = | 170 | let allowed = |
133 | if spec == "*" { HashSet::new() } else { spec.split('|').map(String::from).collect() }; | 171 | if spec == "*" { HashSet::new() } else { spec.split('|').map(String::from).collect() }; |
134 | Filter { depth, allowed, longer_than, version: 0 } | 172 | Filter { depth, allowed, longer_than, heartbeat_longer_than, version: 0 } |
135 | } | 173 | } |
136 | 174 | ||
137 | fn install(mut self) { | 175 | fn install(mut self) { |
@@ -143,9 +181,15 @@ impl Filter { | |||
143 | } | 181 | } |
144 | 182 | ||
145 | struct ProfileStack { | 183 | struct ProfileStack { |
146 | starts: Vec<Instant>, | 184 | frames: Vec<Frame>, |
147 | filter: Filter, | 185 | filter: Filter, |
148 | messages: Tree<Message>, | 186 | messages: Tree<Message>, |
187 | heartbeats: bool, | ||
188 | } | ||
189 | |||
190 | struct Frame { | ||
191 | t: Instant, | ||
192 | heartbeats: u32, | ||
149 | } | 193 | } |
150 | 194 | ||
151 | #[derive(Default)] | 195 | #[derive(Default)] |
@@ -157,35 +201,49 @@ struct Message { | |||
157 | 201 | ||
158 | impl ProfileStack { | 202 | impl ProfileStack { |
159 | fn new() -> ProfileStack { | 203 | fn new() -> ProfileStack { |
160 | ProfileStack { starts: Vec::new(), messages: Tree::default(), filter: Default::default() } | 204 | ProfileStack { |
205 | frames: Vec::new(), | ||
206 | messages: Tree::default(), | ||
207 | filter: Default::default(), | ||
208 | heartbeats: false, | ||
209 | } | ||
161 | } | 210 | } |
162 | 211 | ||
163 | fn push(&mut self, label: Label) -> bool { | 212 | fn push(&mut self, label: Label) -> bool { |
164 | if self.starts.is_empty() { | 213 | if self.frames.is_empty() { |
165 | if let Ok(f) = FILTER.try_read() { | 214 | if let Ok(f) = FILTER.try_read() { |
166 | if f.version > self.filter.version { | 215 | if f.version > self.filter.version { |
167 | self.filter = f.clone(); | 216 | self.filter = f.clone(); |
168 | } | 217 | } |
169 | }; | 218 | }; |
170 | } | 219 | } |
171 | if self.starts.len() > self.filter.depth { | 220 | if self.frames.len() > self.filter.depth { |
172 | return false; | 221 | return false; |
173 | } | 222 | } |
174 | let allowed = &self.filter.allowed; | 223 | let allowed = &self.filter.allowed; |
175 | if self.starts.is_empty() && !allowed.is_empty() && !allowed.contains(label) { | 224 | if self.frames.is_empty() && !allowed.is_empty() && !allowed.contains(label) { |
176 | return false; | 225 | return false; |
177 | } | 226 | } |
178 | 227 | ||
179 | self.starts.push(Instant::now()); | 228 | self.frames.push(Frame { t: Instant::now(), heartbeats: 0 }); |
180 | self.messages.start(); | 229 | self.messages.start(); |
181 | true | 230 | true |
182 | } | 231 | } |
183 | 232 | ||
184 | fn pop(&mut self, label: Label, detail: Option<String>) { | 233 | fn pop(&mut self, label: Label, detail: Option<String>) { |
185 | let start = self.starts.pop().unwrap(); | 234 | let frame = self.frames.pop().unwrap(); |
186 | let duration = start.elapsed(); | 235 | let duration = frame.t.elapsed(); |
236 | |||
237 | if self.heartbeats { | ||
238 | self.heartbeat(frame.heartbeats); | ||
239 | let avg_span = duration / (frame.heartbeats + 1); | ||
240 | if avg_span > self.filter.heartbeat_longer_than { | ||
241 | eprintln!("Too few heartbeats {} ({}/{:?})?", label, frame.heartbeats, duration) | ||
242 | } | ||
243 | } | ||
244 | |||
187 | self.messages.finish(Message { duration, label, detail }); | 245 | self.messages.finish(Message { duration, label, detail }); |
188 | if self.starts.is_empty() { | 246 | if self.frames.is_empty() { |
189 | let longer_than = self.filter.longer_than; | 247 | let longer_than = self.filter.longer_than; |
190 | // Convert to millis for comparison to avoid problems with rounding | 248 | // Convert to millis for comparison to avoid problems with rounding |
191 | // (otherwise we could print `0ms` despite user's `>0` filter when | 249 | // (otherwise we could print `0ms` despite user's `>0` filter when |
@@ -198,6 +256,15 @@ impl ProfileStack { | |||
198 | self.messages.clear(); | 256 | self.messages.clear(); |
199 | } | 257 | } |
200 | } | 258 | } |
259 | |||
260 | fn heartbeats(&mut self, yes: bool) { | ||
261 | self.heartbeats = yes; | ||
262 | } | ||
263 | fn heartbeat(&mut self, n: u32) { | ||
264 | if let Some(frame) = self.frames.last_mut() { | ||
265 | frame.heartbeats += n; | ||
266 | } | ||
267 | } | ||
201 | } | 268 | } |
202 | 269 | ||
203 | fn print( | 270 | fn print( |
diff --git a/crates/profile/src/lib.rs b/crates/profile/src/lib.rs index 79dba47d5..9ca6341db 100644 --- a/crates/profile/src/lib.rs +++ b/crates/profile/src/lib.rs | |||
@@ -10,7 +10,7 @@ mod tree; | |||
10 | use std::cell::RefCell; | 10 | use std::cell::RefCell; |
11 | 11 | ||
12 | pub use crate::{ | 12 | pub use crate::{ |
13 | hprof::{init, init_from, span}, | 13 | hprof::{heartbeat, heartbeat_span, init, init_from, span}, |
14 | memory_usage::{Bytes, MemoryUsage}, | 14 | memory_usage::{Bytes, MemoryUsage}, |
15 | stop_watch::{StopWatch, StopWatchSpan}, | 15 | stop_watch::{StopWatch, StopWatchSpan}, |
16 | }; | 16 | }; |
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index b1beeb883..e74b287fb 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc | |||
@@ -254,23 +254,10 @@ let g:LanguageClient_serverCommands = { | |||
254 | 254 | ||
255 | ==== YouCompleteMe | 255 | ==== YouCompleteMe |
256 | 256 | ||
257 | 1. Install YouCompleteMe by following the instructions | 257 | Install YouCompleteMe by following the instructions |
258 | https://github.com/ycm-core/lsp-examples#rust-rust-analyzer[here] | 258 | https://github.com/ycm-core/YouCompleteMe#installation[here]. |
259 | 259 | ||
260 | 2. Configure by adding this to your vim/neovim config file (replacing the existing Rust-specific line if it exists): | 260 | rust-analyzer is the default in ycm, it should work out of the box. |
261 | + | ||
262 | [source,vim] | ||
263 | ---- | ||
264 | let g:ycm_language_server = | ||
265 | \ [ | ||
266 | \ { | ||
267 | \ 'name': 'rust', | ||
268 | \ 'cmdline': ['rust-analyzer'], | ||
269 | \ 'filetypes': ['rust'], | ||
270 | \ 'project_root_files': ['Cargo.toml'] | ||
271 | \ } | ||
272 | \ ] | ||
273 | ---- | ||
274 | 261 | ||
275 | ==== ALE | 262 | ==== ALE |
276 | 263 | ||