mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-15 09:27:27 +00:00
indent on typing dot. fixes #439
This commit is contained in:
parent
3e42a15878
commit
f99398d9d5
4 changed files with 103 additions and 39 deletions
|
@ -12,41 +12,39 @@ macro_rules! ctry {
|
|||
};
|
||||
}
|
||||
|
||||
mod db;
|
||||
mod imp;
|
||||
mod completion;
|
||||
mod db;
|
||||
mod goto_defenition;
|
||||
mod symbol_index;
|
||||
mod imp;
|
||||
pub mod mock_analysis;
|
||||
mod runnables;
|
||||
mod symbol_index;
|
||||
|
||||
mod extend_selection;
|
||||
mod syntax_highlighting;
|
||||
mod hover;
|
||||
mod syntax_highlighting;
|
||||
|
||||
use std::{fmt, sync::Arc};
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
use ra_syntax::{SourceFileNode, TextRange, TextUnit, SmolStr, SyntaxKind};
|
||||
use ra_syntax::{SmolStr, SourceFileNode, SyntaxKind, TextRange, TextUnit};
|
||||
use ra_text_edit::TextEdit;
|
||||
use rayon::prelude::*;
|
||||
use relative_path::RelativePathBuf;
|
||||
use rustc_hash::FxHashMap;
|
||||
use salsa::ParallelDatabase;
|
||||
|
||||
use crate::symbol_index::{SymbolIndex, FileSymbol};
|
||||
use crate::symbol_index::{FileSymbol, SymbolIndex};
|
||||
|
||||
pub use crate::{
|
||||
completion::{CompletionItem, CompletionItemKind, InsertText},
|
||||
runnables::{Runnable, RunnableKind},
|
||||
};
|
||||
pub use ra_editor::{
|
||||
Fold, FoldKind, HighlightedRange, LineIndex, StructureNode, Severity
|
||||
};
|
||||
pub use hir::FnSignatureInfo;
|
||||
pub use ra_editor::{Fold, FoldKind, HighlightedRange, LineIndex, Severity, StructureNode};
|
||||
|
||||
pub use ra_db::{
|
||||
Canceled, Cancelable, FilePosition, FileRange, LocalSyntaxPtr,
|
||||
CrateGraph, CrateId, SourceRootId, FileId, SyntaxDatabase, FilesDatabase
|
||||
Cancelable, Canceled, CrateGraph, CrateId, FileId, FilePosition, FileRange, FilesDatabase,
|
||||
LocalSyntaxPtr, SourceRootId, SyntaxDatabase,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -346,7 +344,7 @@ impl Analysis {
|
|||
let edit = ra_editor::on_enter(&file, position.offset)?;
|
||||
Some(SourceChange::from_local_edit(position.file_id, edit))
|
||||
}
|
||||
/// Returns an edit which should be applied after `=` was typed. Primaraly,
|
||||
/// Returns an edit which should be applied after `=` was typed. Primarily,
|
||||
/// this works when adding `let =`.
|
||||
// FIXME: use a snippet completion instead of this hack here.
|
||||
pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> {
|
||||
|
@ -354,6 +352,12 @@ impl Analysis {
|
|||
let edit = ra_editor::on_eq_typed(&file, position.offset)?;
|
||||
Some(SourceChange::from_local_edit(position.file_id, edit))
|
||||
}
|
||||
/// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately.
|
||||
pub fn on_dot_typed(&self, position: FilePosition) -> Option<SourceChange> {
|
||||
let file = self.db.source_file(position.file_id);
|
||||
let edit = ra_editor::on_dot_typed(&file, position.offset)?;
|
||||
Some(SourceChange::from_local_edit(position.file_id, edit))
|
||||
}
|
||||
/// Returns a tree representation of symbols in the file. Useful to draw a
|
||||
/// file outline.
|
||||
pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> {
|
||||
|
|
|
@ -16,7 +16,7 @@ pub use self::{
|
|||
line_index::{LineCol, LineIndex},
|
||||
line_index_utils::translate_offset_with_edit,
|
||||
structure::{file_structure, StructureNode},
|
||||
typing::{join_lines, on_enter, on_eq_typed},
|
||||
typing::{join_lines, on_enter, on_dot_typed, on_eq_typed},
|
||||
diagnostics::diagnostics
|
||||
};
|
||||
use ra_text_edit::TextEditBuilder;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::mem;
|
||||
|
||||
use itertools::Itertools;
|
||||
use ra_syntax::{
|
||||
algo::{find_covering_node, find_leaf_at_offset, LeafAtOffset},
|
||||
ast,
|
||||
|
@ -9,9 +10,8 @@ use ra_syntax::{
|
|||
SyntaxNodeRef, TextRange, TextUnit,
|
||||
};
|
||||
use ra_text_edit::text_utils::contains_offset_nonstrict;
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{find_node_at_offset, TextEditBuilder, LocalEdit};
|
||||
use crate::{find_node_at_offset, LocalEdit, TextEditBuilder};
|
||||
|
||||
pub fn join_lines(file: &SourceFileNode, range: TextRange) -> LocalEdit {
|
||||
let range = if range.is_empty() {
|
||||
|
@ -136,6 +136,27 @@ pub fn on_eq_typed(file: &SourceFileNode, offset: TextUnit) -> Option<LocalEdit>
|
|||
})
|
||||
}
|
||||
|
||||
pub fn on_dot_typed(file: &SourceFileNode, offset: TextUnit) -> Option<LocalEdit> {
|
||||
let before_dot_offset = offset - TextUnit::of_char('.');
|
||||
|
||||
let whitespace = find_leaf_at_offset(file.syntax(), before_dot_offset)
|
||||
.left_biased()
|
||||
.and_then(ast::Whitespace::cast)?;
|
||||
|
||||
// whitespace found just left of the dot
|
||||
// TODO: indent is always 4 spaces now. A better heuristic could look on the previous line(s)
|
||||
let indent = " ".to_string();
|
||||
|
||||
let cursor_position = offset + TextUnit::of_str(&indent);;
|
||||
let mut edit = TextEditBuilder::default();
|
||||
edit.insert(before_dot_offset, indent);
|
||||
Some(LocalEdit {
|
||||
label: "indent dot".to_string(),
|
||||
edit: edit.finish(),
|
||||
cursor_position: Some(cursor_position),
|
||||
})
|
||||
}
|
||||
|
||||
fn remove_newline(
|
||||
edit: &mut TextEditBuilder,
|
||||
node: SyntaxNodeRef,
|
||||
|
@ -283,7 +304,9 @@ fn compute_ws(left: SyntaxNodeRef, right: SyntaxNodeRef) -> &'static str {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test_utils::{add_cursor, check_action, extract_offset, extract_range, assert_eq_text};
|
||||
use crate::test_utils::{
|
||||
add_cursor, assert_eq_text, check_action, extract_offset, extract_range,
|
||||
};
|
||||
|
||||
fn check_join_lines(before: &str, after: &str) {
|
||||
check_action(before, after, |file, offset| {
|
||||
|
@ -614,6 +637,31 @@ fn foo() {
|
|||
// ");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_on_dot_typed() {
|
||||
fn do_check(before: &str, after: &str) {
|
||||
let (offset, before) = extract_offset(before);
|
||||
let file = SourceFileNode::parse(&before);
|
||||
let result = on_dot_typed(&file, offset).unwrap();
|
||||
let actual = result.edit.apply(&before);
|
||||
assert_eq_text!(after, &actual);
|
||||
}
|
||||
do_check(
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
.<|>
|
||||
}
|
||||
",
|
||||
r"
|
||||
pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
|
||||
self.child_impl(db, name)
|
||||
.
|
||||
}
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_on_enter() {
|
||||
fn apply_on_enter(before: &str) -> Option<String> {
|
||||
|
|
|
@ -2,15 +2,16 @@ use std::collections::HashMap;
|
|||
|
||||
use gen_lsp_server::ErrorCode;
|
||||
use languageserver_types::{
|
||||
CodeActionResponse, Command, Diagnostic,
|
||||
DiagnosticSeverity, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind,
|
||||
FoldingRangeParams, Location, MarkupContent, MarkupKind, MarkedString, Position,
|
||||
PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit,
|
||||
Range, WorkspaceEdit, ParameterInformation, ParameterLabel, SignatureInformation, Hover,
|
||||
HoverContents, DocumentFormattingParams, DocumentHighlight,
|
||||
CodeActionResponse, Command, Diagnostic, DiagnosticSeverity, DocumentFormattingParams,
|
||||
DocumentHighlight, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind,
|
||||
FoldingRangeParams, Hover, HoverContents, Location, MarkedString, MarkupContent, MarkupKind,
|
||||
ParameterInformation, ParameterLabel, Position, PrepareRenameResponse, Range, RenameParams,
|
||||
SignatureInformation, SymbolInformation, TextDocumentIdentifier, TextEdit, WorkspaceEdit,
|
||||
};
|
||||
use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FileRange, FilePosition, Severity};
|
||||
use ra_syntax::{TextUnit, text_utils::intersect};
|
||||
use ra_analysis::{
|
||||
FileId, FilePosition, FileRange, FoldKind, Query, RunnableKind, Severity, SourceChange,
|
||||
};
|
||||
use ra_syntax::{text_utils::intersect, TextUnit};
|
||||
use ra_text_edit::text_utils::contains_offset_nonstrict;
|
||||
use rustc_hash::FxHashMap;
|
||||
use serde_json::to_value;
|
||||
|
@ -92,7 +93,7 @@ pub fn handle_on_type_formatting(
|
|||
world: ServerWorld,
|
||||
params: req::DocumentOnTypeFormattingParams,
|
||||
) -> Result<Option<Vec<TextEdit>>> {
|
||||
if params.ch != "=" {
|
||||
if params.ch != "=" || params.ch != "." {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
|
@ -102,19 +103,30 @@ pub fn handle_on_type_formatting(
|
|||
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()
|
||||
.edit
|
||||
.as_atoms()
|
||||
.iter()
|
||||
.map_conv_with(&line_index)
|
||||
.collect(),
|
||||
};
|
||||
Ok(Some(edits))
|
||||
|
||||
let analysis: Vec<Box<Fn(FilePosition) -> Option<SourceChange>>> = vec![
|
||||
Box::new(|pos| world.analysis().on_eq_typed(pos)),
|
||||
Box::new(|pos| world.analysis().on_dot_typed(pos)),
|
||||
];
|
||||
|
||||
// try all analysis until one succeeds
|
||||
for ana in analysis {
|
||||
if let Some(mut action) = ana(position) {
|
||||
return Ok(Some(
|
||||
action
|
||||
.source_file_edits
|
||||
.pop()
|
||||
.unwrap()
|
||||
.edit
|
||||
.as_atoms()
|
||||
.iter()
|
||||
.map_conv_with(&line_index)
|
||||
.collect(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
pub fn handle_document_symbol(
|
||||
|
|
Loading…
Reference in a new issue