Repository: rust-analyzer/rust-analyzer
Pull request #194 (2018-11-05 12:03:53)

Introduce FilePosition


Patch:
diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs
index 766df1d962..7c3476e5c2 100644
--- a/crates/ra_analysis/src/completion.rs
+++ b/crates/ra_analysis/src/completion.rs
@@ -14,7 +14,7 @@ use crate::{
     descriptors::module::{ModuleId, ModuleScope, ModuleTree, ModuleSource},
     descriptors::DescriptorDatabase,
     input::FilesDatabase,
-    Cancelable, FileId,
+    Cancelable, FilePosition,
 };
 
 #[derive(Debug)]
@@ -29,21 +29,21 @@ pub struct CompletionItem {
 
 pub(crate) fn resolve_based_completion(
     db: &db::RootDatabase,
-    file_id: FileId,
-    offset: TextUnit,
+    position: FilePosition,
 ) -> Cancelable<Option<Vec<CompletionItem>>> {
-    let source_root_id = db.file_source_root(file_id);
-    let file = db.file_syntax(file_id);
+    let source_root_id = db.file_source_root(position.file_id);
+    let file = db.file_syntax(position.file_id);
     let module_tree = db.module_tree(source_root_id)?;
-    let module_id = match module_tree.any_module_for_source(ModuleSource::File(file_id)) {
+    let module_id = match module_tree.any_module_for_source(ModuleSource::File(position.file_id)) {
         None => return Ok(None),
         Some(it) => it,
     };
     let file = {
-        let edit = AtomEdit::insert(offset, "intellijRulezz".to_string());
+        let edit = AtomEdit::insert(position.offset, "intellijRulezz".to_string());
         file.reparse(&edit)
     };
-    let target_module_id = match find_target_module(&module_tree, module_id, &file, offset) {
+    let target_module_id = match find_target_module(&module_tree, module_id, &file, position.offset)
+    {
         None => return Ok(None),
         Some(it) => it,
     };
@@ -99,18 +99,17 @@ fn crate_path(name_ref: ast::NameRef) -> Option<Vec<ast::NameRef>> {
 
 pub(crate) fn scope_completion(
     db: &db::RootDatabase,
-    file_id: FileId,
-    offset: TextUnit,
+    position: FilePosition,
 ) -> Option<Vec<CompletionItem>> {
-    let original_file = db.file_syntax(file_id);
+    let original_file = db.file_syntax(position.file_id);
     // Insert a fake ident to get a valid parse tree
     let file = {
-        let edit = AtomEdit::insert(offset, "intellijRulezz".to_string());
+        let edit = AtomEdit::insert(position.offset, "intellijRulezz".to_string());
         original_file.reparse(&edit)
     };
     let mut has_completions = false;
     let mut res = Vec::new();
-    if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), offset) {
+    if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) {
         has_completions = true;
         complete_name_ref(&file, name_ref, &mut res);
         // special case, `trait T { fn foo(i_am_a_name_ref) {} }`
@@ -129,7 +128,7 @@ pub(crate) fn scope_completion(
             _ => (),
         }
     }
-    if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), offset) {
+    if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) {
         if is_node::<ast::Param>(name.syntax()) {
             has_completions = true;
             param_completions(name.syntax(), &mut res);
@@ -383,7 +382,7 @@ mod tests {
 
     fn check_scope_completion(code: &str, expected_completions: &str) {
         let (analysis, position) = single_file_with_position(code);
-        let completions = scope_completion(&analysis.imp.db, position.file_id, position.offset)
+        let completions = scope_completion(&analysis.imp.db, position)
             .unwrap()
             .into_iter()
             .filter(|c| c.snippet.is_none())
@@ -393,7 +392,7 @@ mod tests {
 
     fn check_snippet_completion(code: &str, expected_completions: &str) {
         let (analysis, position) = single_file_with_position(code);
-        let completions = scope_completion(&analysis.imp.db, position.file_id, position.offset)
+        let completions = scope_completion(&analysis.imp.db, position)
             .unwrap()
             .into_iter()
             .filter(|c| c.snippet.is_some())
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index 704648b595..f2482559fc 100644
--- a/crates/ra_analysis/src/imp.rs
+++ b/crates/ra_analysis/src/imp.rs
@@ -27,7 +27,7 @@ use crate::{
     input::{FilesDatabase, SourceRoot, SourceRootId, WORKSPACE},
     symbol_index::SymbolIndex,
     AnalysisChange, Cancelable, CrateGraph, CrateId, Diagnostic, FileId, FileResolver,
-    FileSystemEdit, Position, Query, SourceChange, SourceFileEdit,
+    FileSystemEdit, FilePosition, Query, SourceChange, SourceFileEdit,
 };
 
 #[derive(Clone, Debug)]
@@ -220,16 +220,13 @@ impl AnalysisImpl {
         let source_root = self.db.file_source_root(file_id);
         self.db.module_tree(source_root)
     }
-    pub fn parent_module(
-        &self,
-        file_id: FileId,
-        offset: TextUnit,
-    ) -> Cancelable<Vec<(FileId, FileSymbol)>> {
-        let module_tree = self.module_tree(file_id)?;
-        let file = self.db.file_syntax(file_id);
-        let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), offset) {
-            Some(m) if !m.has_semi() => ModuleSource::new_inline(file_id, m),
-            _ => ModuleSource::File(file_id),
+    pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> {
+        let module_tree = self.module_tree(position.file_id)?;
+        let file = self.db.file_syntax(position.file_id);
+        let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset)
+        {
+            Some(m) if !m.has_semi() => ModuleSource::new_inline(position.file_id, m),
+            _ => ModuleSource::File(position.file_id),
         };
 
         let res = module_tree
@@ -269,18 +266,14 @@ impl AnalysisImpl {
     pub fn crate_root(&self, crate_id: CrateId) -> FileId {
         self.db.crate_graph().crate_roots[&crate_id]
     }
-    pub fn completions(
-        &self,
-        file_id: FileId,
-        offset: TextUnit,
-    ) -> Cancelable<Option<Vec<CompletionItem>>> {
+    pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> {
         let mut res = Vec::new();
         let mut has_completions = false;
-        if let Some(scope_based) = scope_completion(&self.db, file_id, offset) {
+        if let Some(scope_based) = scope_completion(&self.db, position) {
             res.extend(scope_based);
             has_completions = true;
         }
-        if let Some(scope_based) = resolve_based_completion(&self.db, file_id, offset)? {
+        if let Some(scope_based) = resolve_based_completion(&self.db, position)? {
             res.extend(scope_based);
             has_completions = true;
         }
@@ -289,18 +282,19 @@ impl AnalysisImpl {
     }
     pub fn approximately_resolve_symbol(
         &self,
-        file_id: FileId,
-        offset: TextUnit,
+        position: FilePosition,
     ) -> Cancelable<Vec<(FileId, FileSymbol)>> {
-        let module_tree = self.module_tree(file_id)?;
-        let file = self.db.file_syntax(file_id);
+        let module_tree = self.module_tree(position.file_id)?;
+        let file = self.db.file_syntax(position.file_id);
         let syntax = file.syntax();
-        if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, offset) {
+        if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) {
             // First try to resolve the symbol locally
-            return if let Some((name, range)) = resolve_local_name(&self.db, file_id, name_ref) {
+            return if let Some((name, range)) =
+                resolve_local_name(&self.db, position.file_id, name_ref)
+            {
                 let mut vec = vec![];
                 vec.push((
-                    file_id,
+                    position.file_id,
                     FileSymbol {
                         name,
                         node_range: range,
@@ -313,10 +307,10 @@ impl AnalysisImpl {
                 self.index_resolve(name_ref)
             };
         }
-        if let Some(name) = find_node_at_offset::<ast::Name>(syntax, offset) {
+        if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) {
             if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) {
                 if module.has_semi() {
-                    let file_ids = self.resolve_module(&*module_tree, file_id, module);
+                    let file_ids = self.resolve_module(&*module_tree, position.file_id, module);
 
                     let res = file_ids
                         .into_iter()
@@ -341,16 +335,17 @@ impl AnalysisImpl {
         Ok(vec![])
     }
 
-    pub fn find_all_refs(&self, file_id: FileId, offset: TextUnit) -> Vec<(FileId, TextRange)> {
-        let file = self.db.file_syntax(file_id);
+    pub fn find_all_refs(&self, position: FilePosition) -> Vec<(FileId, TextRange)> {
+        let file = self.db.file_syntax(position.file_id);
         let syntax = file.syntax();
 
         // Find the binding associated with the offset
-        let maybe_binding = find_node_at_offset::<ast::BindPat>(syntax, offset).or_else(|| {
-            let name_ref = find_node_at_offset::<ast::NameRef>(syntax, offset)?;
-            let resolved = resolve_local_name(&self.db, file_id, name_ref)?;
-            find_node_at_offset::<ast::BindPat>(syntax, resolved.1.end())
-        });
+        let maybe_binding =
+            find_node_at_offset::<ast::BindPat>(syntax, position.offset).or_else(|| {
+                let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?;
+                let resolved = resolve_local_name(&self.db, position.file_id, name_ref)?;
+                find_node_at_offset::<ast::BindPat>(syntax, resolved.1.end())
+            });
 
         let binding = match maybe_binding {
             None => return Vec::new(),
@@ -359,11 +354,11 @@ impl AnalysisImpl {
 
         let decl = DeclarationDescriptor::new(binding);
 
-        let mut ret = vec![(file_id, decl.range)];
+        let mut ret = vec![(position.file_id, decl.range)];
         ret.extend(
             decl.find_all_refs()
                 .into_iter()
-                .map(|ref_desc| (file_id, ref_desc.range)),
+                .map(|ref_desc| (position.file_id, ref_desc.range)),
         );
 
         ret
@@ -457,14 +452,13 @@ impl AnalysisImpl {
 
     pub fn resolve_callable(
         &self,
-        file_id: FileId,
-        offset: TextUnit,
+        position: FilePosition,
     ) -> Cancelable<Option<(FnDescriptor, Option<usize>)>> {
-        let file = self.db.file_syntax(file_id);
+        let file = self.db.file_syntax(position.file_id);
         let syntax = file.syntax();
 
         // Find the calling expression and it's NameRef
-        let calling_node = match FnCallNode::with_node(syntax, offset) {
+        let calling_node = match FnCallNode::with_node(syntax, position.offset) {
             Some(node) => node,
             None => return Ok(None),
         };
@@ -499,7 +493,7 @@ impl AnalysisImpl {
                             if let Some(ref arg_list) = calling_node.arg_list() {
                                 let start = arg_list.syntax().range().start();
 
-                                let range_search = TextRange::from_to(start, offset);
+                                let range_search = TextRange::from_to(start, position.offset);
                                 let mut commas: usize = arg_list
                                     .syntax()
                                     .text()
@@ -568,7 +562,7 @@ impl SourceChange {
             file_system_edits: vec![],
             cursor_position: edit
                 .cursor_position
-                .map(|offset| Position { offset, file_id }),
+                .map(|offset| FilePosition { offset, file_id }),
         }
     }
 }
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index fee382151a..0ea9ebee7b 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -119,18 +119,18 @@ impl AnalysisHost {
     }
 }
 
+#[derive(Clone, Copy, Debug)]
+pub struct FilePosition {
+    pub file_id: FileId,
+    pub offset: TextUnit,
+}
+
 #[derive(Debug)]
 pub struct SourceChange {
     pub label: String,
     pub source_file_edits: Vec<SourceFileEdit>,
     pub file_system_edits: Vec<FileSystemEdit>,
-    pub cursor_position: Option<Position>,
-}
-
-#[derive(Debug)]
-pub struct Position {
-    pub file_id: FileId,
-    pub offset: TextUnit,
+    pub cursor_position: Option<FilePosition>,
 }
 
 #[derive(Debug)]
@@ -224,18 +224,18 @@ impl Analysis {
         let file = self.imp.file_syntax(file_id);
         SourceChange::from_local_edit(file_id, "join lines", ra_editor::join_lines(&file, range))
     }
-    pub fn on_enter(&self, file_id: FileId, offset: TextUnit) -> Option<SourceChange> {
-        let file = self.imp.file_syntax(file_id);
-        let edit = ra_editor::on_enter(&file, offset)?;
-        let res = SourceChange::from_local_edit(file_id, "on enter", edit);
+    pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> {
+        let file = self.imp.file_syntax(position.file_id);
+        let edit = ra_editor::on_enter(&file, position.offset)?;
+        let res = SourceChange::from_local_edit(position.file_id, "on enter", edit);
         Some(res)
     }
-    pub fn on_eq_typed(&self, file_id: FileId, offset: TextUnit) -> Option<SourceChange> {
-        let file = self.imp.file_syntax(file_id);
+    pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> {
+        let file = self.imp.file_syntax(position.file_id);
         Some(SourceChange::from_local_edit(
-            file_id,
+            position.file_id,
             "add semicolon",
-            ra_editor::on_eq_typed(&file, offset)?,
+            ra_editor::on_eq_typed(&file, position.offset)?,
         ))
     }
     pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> {
@@ -251,24 +251,15 @@ impl Analysis {
     }
     pub fn approximately_resolve_symbol(
         &self,
-        file_id: FileId,
-        offset: TextUnit,
+        position: FilePosition,
     ) -> Cancelable<Vec<(FileId, FileSymbol)>> {
-        self.imp.approximately_resolve_symbol(file_id, offset)
+        self.imp.approximately_resolve_symbol(position)
     }
-    pub fn find_all_refs(
-        &self,
-        file_id: FileId,
-        offset: TextUnit,
-    ) -> Cancelable<Vec<(FileId, TextRange)>> {
-        Ok(self.imp.find_all_refs(file_id, offset))
+    pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> {
+        Ok(self.imp.find_all_refs(position))
     }
-    pub fn parent_module(
-        &self,
-        file_id: FileId,
-        offset: TextUnit,
-    ) -> Cancelable<Vec<(FileId, FileSymbol)>> {
-        self.imp.parent_module(file_id, offset)
+    pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> {
+        self.imp.parent_module(position)
     }
     pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
         self.imp.crate_for(file_id)
@@ -284,12 +275,8 @@ impl Analysis {
         let file = self.imp.file_syntax(file_id);
         Ok(ra_editor::highlight(&file))
     }
-    pub fn completions(
-        &self,
-        file_id: FileId,
-        offset: TextUnit,
-    ) -> Cancelable<Option<Vec<CompletionItem>>> {
-        self.imp.completions(file_id, offset)
+    pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> {
+        self.imp.completions(position)
     }
     pub fn assists(&self, file_id: FileId, range: TextRange) -> Cancelable<Vec<SourceChange>> {
         Ok(self.imp.assists(file_id, range))
@@ -299,10 +286,9 @@ impl Analysis {
     }
     pub fn resolve_callable(
         &self,
-        file_id: FileId,
-        offset: TextUnit,
+        position: FilePosition,
     ) -> Cancelable<Option<(FnDescriptor, Option<usize>)>> {
-        self.imp.resolve_callable(file_id, offset)
+        self.imp.resolve_callable(position)
     }
 }
 
diff --git a/crates/ra_analysis/src/mock_analysis.rs b/crates/ra_analysis/src/mock_analysis.rs
index a7134a0e66..8e8f969f45 100644
--- a/crates/ra_analysis/src/mock_analysis.rs
+++ b/crates/ra_analysis/src/mock_analysis.rs
@@ -1,16 +1,9 @@
 use std::sync::Arc;
 
-use ra_syntax::TextUnit;
 use relative_path::{RelativePath, RelativePathBuf};
 use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER};
 
-use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FileResolver};
-
-#[derive(Debug)]
-pub struct FilePosition {
-    pub file_id: FileId,
-    pub offset: TextUnit,
-}
+use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FileResolver, FilePosition};
 
 /// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis
 /// from a set of in-memory files.
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs
index 84ffac2daa..fa04f4b00b 100644
--- a/crates/ra_lsp_server/src/conv.rs
+++ b/crates/ra_lsp_server/src/conv.rs
@@ -2,7 +2,7 @@ use languageserver_types::{
     Location, Position, Range, SymbolKind, TextDocumentEdit, TextDocumentIdentifier,
     TextDocumentItem, TextDocumentPositionParams, TextEdit, Url, VersionedTextDocumentIdentifier,
 };
-use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit};
+use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit, FilePosition};
 use ra_editor::{AtomEdit, Edit, LineCol, LineIndex};
 use ra_syntax::{SyntaxKind, TextRange, TextUnit};
 
@@ -165,6 +165,17 @@ impl<'a> TryConvWith for &'a TextDocumentIdentifier {
     }
 }
 
+impl<'a> TryConvWith for &'a TextDocumentPositionParams {
+    type Ctx = ServerWorld;
+    type Output = FilePosition;
+    fn try_conv_with(self, world: &ServerWorld) -> Result<FilePosition> {
+        let file_id = self.text_document.try_conv_with(world)?;
+        let line_index = world.analysis().file_line_index(file_id);
+        let offset = self.position.conv_with(&line_index);
+        Ok(FilePosition { file_id, offset })
+    }
+}
+
 impl<T: TryConvWith> TryConvWith for Vec<T> {
     type Ctx = <T as TryConvWith>::Ctx;
     type Output = Vec<<T as TryConvWith>::Output>;
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index 2219a00365..5314a333e8 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -6,9 +6,9 @@ use languageserver_types::{
     DiagnosticSeverity, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind,
     FoldingRangeParams, InsertTextFormat, Location, MarkupContent, MarkupKind, Position,
     PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit,
-    WorkspaceEdit,
+    WorkspaceEdit, ParameterInformation, SignatureInformation,
 };
-use ra_analysis::{FileId, FoldKind, Query, RunnableKind};
+use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FilePosition};
 use ra_syntax::text_utils::contains_offset_nonstrict;
 use rustc_hash::FxHashMap;
 use serde_json::to_value;
@@ -83,10 +83,8 @@ pub fn handle_on_enter(
     world: ServerWorld,
     params: req::TextDocumentPositionParams,
 ) -> Result<Option<req::SourceChange>> {
-    let file_id = params.text_document.try_conv_with(&world)?;
-    let line_index = world.analysis().file_line_index(file_id);
-    let offset = params.position.conv_with(&line_index);
-    match world.analysis().on_enter(file_id, offset) {
+    let position = params.try_conv_with(&world)?;
+    match world.analysis().on_enter(position) {
         None => Ok(None),
         Some(edit) => Ok(Some(edit.try_conv_with(&world)?)),
     }
@@ -102,8 +100,11 @@ pub fn handle_on_type_formatting(
 
     let file_id = params.text_document.try_conv_with(&world)?;
     let line_index = world.analysis().file_line_index(file_id);
-    let offset = params.position.conv_with(&line_index);
-    let edits = match world.analysis().on_eq_typed(file_id, offset) {
+    let position = FilePosition {
+        file_id,
+        offset: params.position.conv_with(&line_index),
+    };
+    let edits = match world.analysis().on_eq_typed(position) {
         None => return Ok(None),
         Some(mut action) => action.source_file_edits.pop().unwrap().edits,
     };
@@ -201,14 +202,9 @@ pub fn handle_goto_definition(
     world: ServerWorld,
     params: req::TextDocumentPositionParams,
 ) -> Result<Option<req::GotoDefinitionResponse>> {
-    let file_id = params.text_document.try_conv_with(&world)?;
-    let line_index = world.analysis().file_line_index(file_id);
-    let offset = params.position.conv_with(&line_index);
+    let position = params.try_conv_with(&world)?;
     let mut res = Vec::new();
-    for (file_id, symbol) in world
-        .analysis()
-        .approximately_resolve_symbol(file_id, offset)?
-    {
+    for (file_id, symbol) in world.analysis().approximately_resolve_symbol(position)? {
         let line_index = world.analysis().file_line_index(file_id);
         let location = to_location(file_id, symbol.node_range, &world, &line_index)?;
         res.push(location)
@@ -220,11 +216,9 @@ pub fn handle_parent_module(
     world: ServerWorld,
     params: req::TextDocumentPositionParams,
 ) -> Result<Vec<Location>> {
-    let file_id = params.text_document.try_conv_with(&world)?;
-    let line_index = world.analysis().file_line_index(file_id);
-    let offset = params.position.conv_with(&line_index);
+    let position = params.try_conv_with(&world)?;
     let mut res = Vec::new();
-    for (file_id, symbol) in world.analysis().parent_module(file_id, offset)? {
+    for (file_id, symbol) in world.analysis().parent_module(position)? {
         let line_index = world.analysis().file_line_index(file_id);
         let location = to_location(file_id, symbol.node_range, &world, &line_index)?;
         res.push(location);
@@ -381,10 +375,13 @@ pub fn handle_completion(
     world: ServerWorld,
     params: req::CompletionParams,
 ) -> Result<Option<req::CompletionResponse>> {
-    let file_id = params.text_document.try_conv_with(&world)?;
-    let line_index = world.analysis().file_line_index(file_id);
-    let offset = params.position.conv_with(&line_index);
-    let items = match world.analysis().completions(file_id, offset)? {
+    let position = {
+        let file_id = params.text_document.try_conv_with(&world)?;
+        let line_index = world.analysis().file_line_index(file_id);
+        let offset = params.position.conv_with(&line_index);
+        FilePosition { file_id, offset }
+    };
+    let items = match world.analysis().completions(position)? {
         None => return Ok(None),
         Some(items) => items,
     };
@@ -444,13 +441,9 @@ pub fn handle_signature_help(
     world: ServerWorld,
     params: req::TextDocumentPositionParams,
 ) -> Result<Option<req::SignatureHelp>> {
-    use languageserver_types::{ParameterInformation, SignatureInformation};
+    let position = params.try_conv_with(&world)?;
 
-    let file_id = params.text_document.try_conv_with(&world)?;
-    let line_index = world.analysis().file_line_index(file_id);
-    let offset = params.position.conv_with(&line_index);
-
-    if let Some((descriptor, active_param)) = world.analysis().resolve_callable(file_id, offset)? {
+    if let Some((descriptor, active_param)) = world.analysis().resolve_callable(position)? {
         let parameters: Vec<ParameterInformation> = descriptor
             .params
             .iter()
@@ -489,18 +482,17 @@ pub fn handle_prepare_rename(
     world: ServerWorld,
     params: req::TextDocumentPositionParams,
 ) -> Result<Option<PrepareRenameResponse>> {
-    let file_id = params.text_document.try_conv_with(&world)?;
-    let line_index = world.analysis().file_line_index(file_id);
-    let offset = params.position.conv_with(&line_index);
+    let position = params.try_conv_with(&world)?;
 
     // We support renaming references like handle_rename does.
     // In the future we may want to reject the renaming of things like keywords here too.
-    let refs = world.analysis().find_all_refs(file_id, offset)?;
-    if refs.is_empty() {
-        return Ok(None);
-    }
-
-    let r = refs.first().unwrap();
+    let refs = world.analysis().find_all_refs(position)?;
+    let r = match refs.first() {
+        Some(r) => r,
+        None => return Ok(None),
+    };
+    let file_id = params.text_document.try_conv_with(&world)?;
+    let line_index = world.analysis().file_line_index(file_id);
     let loc = to_location(r.0, r.1, &world, &line_index)?;
 
     Ok(Some(PrepareRenameResponse::Range(loc.range)))
@@ -519,7 +511,9 @@ pub fn handle_rename(world: ServerWorld, params: RenameParams) -> Result<Option<
         .into());
     }
 
-    let refs = world.analysis().find_all_refs(file_id, offset)?;
+    let refs = world
+        .analysis()
+        .find_all_refs(FilePosition { file_id, offset })?;
     if refs.is_empty() {
         return Ok(None);
     }
@@ -550,7 +544,9 @@ pub fn handle_references(
     let line_index = world.analysis().file_line_index(file_id);
     let offset = params.position.conv_with(&line_index);
 
-    let refs = world.analysis().find_all_refs(file_id, offset)?;
+    let refs = world
+        .analysis()
+        .find_all_refs(FilePosition { file_id, offset })?;
 
     Ok(Some(
         refs.into_iter()


Test patch:
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs
index 7f7bb8e6bd..c605d34f0b 100644
--- a/crates/ra_analysis/tests/tests.rs
+++ b/crates/ra_analysis/tests/tests.rs
@@ -15,10 +15,7 @@ use ra_analysis::{
 
 fn get_signature(text: &str) -> (FnDescriptor, Option<usize>) {
     let (analysis, position) = single_file_with_position(text);
-    analysis
-        .resolve_callable(position.file_id, position.offset)
-        .unwrap()
-        .unwrap()
+    analysis.resolve_callable(position).unwrap().unwrap()
 }
 
 #[test]
@@ -32,9 +29,7 @@ fn test_resolve_module() {
     ",
     );
 
-    let symbols = analysis
-        .approximately_resolve_symbol(pos.file_id, pos.offset)
-        .unwrap();
+    let symbols = analysis.approximately_resolve_symbol(pos).unwrap();
     assert_eq_dbg(
         r#"[(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })]"#,
         &symbols,
@@ -49,9 +44,7 @@ fn test_resolve_module() {
     ",
     );
 
-    let symbols = analysis
-        .approximately_resolve_symbol(pos.file_id, pos.offset)
-        .unwrap();
+    let symbols = analysis.approximately_resolve_symbol(pos).unwrap();
     assert_eq_dbg(
         r#"[(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })]"#,
         &symbols,
@@ -92,7 +85,7 @@ fn test_resolve_parent_module() {
         <|>// empty
     ",
     );
-    let symbols = analysis.parent_module(pos.file_id, pos.offset).unwrap();
+    let symbols = analysis.parent_module(pos).unwrap();
     assert_eq_dbg(
         r#"[(FileId(1), FileSymbol { name: "foo", node_range: [4; 7), kind: MODULE })]"#,
         &symbols,
@@ -111,7 +104,7 @@ fn test_resolve_parent_module_for_inline() {
         }
     ",
     );
-    let symbols = analysis.parent_module(pos.file_id, pos.offset).unwrap();
+    let symbols = analysis.parent_module(pos).unwrap();
     assert_eq_dbg(
         r#"[(FileId(1), FileSymbol { name: "bar", node_range: [18; 21), kind: MODULE })]"#,
         &symbols,
@@ -397,9 +390,7 @@ By default this method stops actor's `Context`."#
 
 fn get_all_refs(text: &str) -> Vec<(FileId, TextRange)> {
     let (analysis, position) = single_file_with_position(text);
-    analysis
-        .find_all_refs(position.file_id, position.offset)
-        .unwrap()
+    analysis.find_all_refs(position).unwrap()
 }
 
 #[test]
@@ -454,10 +445,7 @@ fn test_complete_crate_path() {
         use crate::Sp<|>
     ",
     );
-    let completions = analysis
-        .completions(position.file_id, position.offset)
-        .unwrap()
-        .unwrap();
+    let completions = analysis.completions(position).unwrap().unwrap();
     assert_eq_dbg(
         r#"[CompletionItem { label: "foo", lookup: None, snippet: None },
             CompletionItem { label: "Spam", lookup: None, snippet: None }]"#,