aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/base_db/src/lib.rs3
-rw-r--r--crates/ide/src/doc_links.rs63
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs32
-rw-r--r--crates/profile/src/hprof.rs87
-rw-r--r--crates/profile/src/lib.rs2
-rw-r--r--docs/user/manual.adoc19
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
69impl<T: salsa::Database> CheckCanceled for T { 71impl<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.
102pub(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.
101pub(crate) fn extract_definitions_from_markdown( 124pub(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.
332pub(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.
354fn map_links<'e>( 355fn 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
13use crate::{ 13use 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
17use super::{highlights::Highlights, injector::Injector}; 18use 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
286fn 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
313fn module_def_to_hl_tag(def: hir::ModuleDef) -> HlTag { 287fn 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]
73pub fn heartbeat_span() -> HeartbeatSpan {
74 let enabled = PROFILING_ENABLED.load(Ordering::Relaxed);
75 HeartbeatSpan::new(enabled)
76}
77
78#[inline]
79pub fn heartbeat() {
80 let enabled = PROFILING_ENABLED.load(Ordering::Relaxed);
81 if enabled {
82 with_profile_stack(|it| it.heartbeat(1));
83 }
84}
85
72pub struct ProfileSpan(Option<ProfilerImpl>); 86pub struct ProfileSpan(Option<ProfilerImpl>);
73 87
74struct ProfilerImpl { 88struct ProfilerImpl {
@@ -92,6 +106,28 @@ impl Drop for ProfilerImpl {
92 } 106 }
93} 107}
94 108
109pub struct HeartbeatSpan {
110 enabled: bool,
111}
112
113impl 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
123impl Drop for HeartbeatSpan {
124 fn drop(&mut self) {
125 if self.enabled {
126 with_profile_stack(|it| it.heartbeats(false))
127 }
128 }
129}
130
95static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false); 131static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false);
96static FILTER: Lazy<RwLock<Filter>> = Lazy::new(Default::default); 132static 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
145struct ProfileStack { 183struct 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
190struct 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
158impl ProfileStack { 202impl 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
203fn print( 270fn 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;
10use std::cell::RefCell; 10use std::cell::RefCell;
11 11
12pub use crate::{ 12pub 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
2571. Install YouCompleteMe by following the instructions 257Install 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
2602. Configure by adding this to your vim/neovim config file (replacing the existing Rust-specific line if it exists): 260rust-analyzer is the default in ycm, it should work out of the box.
261+
262[source,vim]
263----
264let 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