Don't mess with cursor position when adding hashes

This commit is contained in:
Aleksey Kladov 2020-07-09 19:21:41 +02:00
parent 1fb92d791e
commit 68706b59c9
4 changed files with 34 additions and 16 deletions

View file

@ -1,5 +1,7 @@
use std::borrow::Cow;
use ra_syntax::{ use ra_syntax::{
ast::{self, HasStringValue}, ast::{self, HasQuotes, HasStringValue},
AstToken, AstToken,
SyntaxKind::{RAW_STRING, STRING}, SyntaxKind::{RAW_STRING, STRING},
TextSize, TextSize,
@ -32,14 +34,17 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext) -> Option<
target, target,
|edit| { |edit| {
let max_hash_streak = count_hashes(&value); let max_hash_streak = count_hashes(&value);
let mut hashes = String::with_capacity(max_hash_streak + 1); let hashes = "#".repeat(max_hash_streak + 1);
for _ in 0..hashes.capacity() { if matches!(value, Cow::Borrowed(_)) {
hashes.push('#'); // Avoid replacing the whole string to better position the cursor.
} edit.insert(token.syntax().text_range().start(), format!("r{}", hashes));
edit.insert(token.syntax().text_range().end(), format!("{}", hashes));
} else {
edit.replace( edit.replace(
token.syntax().text_range(), token.syntax().text_range(),
format!("r{}\"{}\"{}", hashes, value, hashes), format!("r{}\"{}\"{}", hashes, value, hashes),
); );
}
}, },
) )
} }
@ -70,6 +75,14 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext) -> Optio
|edit| { |edit| {
// parse inside string to escape `"` // parse inside string to escape `"`
let escaped = value.escape_default().to_string(); let escaped = value.escape_default().to_string();
if let Some(offsets) = token.quote_offsets() {
if token.text()[offsets.contents - token.syntax().text_range().start()] == escaped {
edit.replace(offsets.quotes.0, "\"");
edit.replace(offsets.quotes.1, "\"");
return;
}
}
edit.replace(token.syntax().text_range(), format!("\"{}\"", escaped)); edit.replace(token.syntax().text_range(), format!("\"{}\"", escaped));
}, },
) )

View file

@ -272,7 +272,7 @@ fn format_args_expand(
fn unquote_str(lit: &tt::Literal) -> Option<String> { fn unquote_str(lit: &tt::Literal) -> Option<String> {
let lit = ast::make::tokens::literal(&lit.to_string()); let lit = ast::make::tokens::literal(&lit.to_string());
let token = ast::String::cast(lit)?; let token = ast::String::cast(lit)?;
token.value() token.value().map(|it| it.into_owned())
} }
fn concat_expand( fn concat_expand(

View file

@ -25,7 +25,7 @@ pub(super) fn highlight_injection(
return None; return None;
} }
let value = literal.value()?; let value = literal.value()?;
let (analysis, tmp_file_id) = Analysis::from_single_file(value); let (analysis, tmp_file_id) = Analysis::from_single_file(value.into_owned());
if let Some(range) = literal.open_quote_text_range() { if let Some(range) = literal.open_quote_text_range() {
acc.add(HighlightedRange { acc.add(HighlightedRange {

View file

@ -1,6 +1,9 @@
//! There are many AstNodes, but only a few tokens, so we hand-write them here. //! There are many AstNodes, but only a few tokens, so we hand-write them here.
use std::convert::{TryFrom, TryInto}; use std::{
borrow::Cow,
convert::{TryFrom, TryInto},
};
use crate::{ use crate::{
ast::{AstToken, Comment, RawString, String, Whitespace}, ast::{AstToken, Comment, RawString, String, Whitespace},
@ -138,11 +141,11 @@ impl HasQuotes for String {}
impl HasQuotes for RawString {} impl HasQuotes for RawString {}
pub trait HasStringValue: HasQuotes { pub trait HasStringValue: HasQuotes {
fn value(&self) -> Option<std::string::String>; fn value(&self) -> Option<Cow<'_, str>>;
} }
impl HasStringValue for String { impl HasStringValue for String {
fn value(&self) -> Option<std::string::String> { fn value(&self) -> Option<Cow<'_, str>> {
let text = self.text().as_str(); let text = self.text().as_str();
let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
@ -156,15 +159,17 @@ impl HasStringValue for String {
if has_error { if has_error {
return None; return None;
} }
Some(buf) // FIXME: don't actually allocate for borrowed case
let res = if buf == text { Cow::Borrowed(text) } else { Cow::Owned(buf) };
Some(res)
} }
} }
impl HasStringValue for RawString { impl HasStringValue for RawString {
fn value(&self) -> Option<std::string::String> { fn value(&self) -> Option<Cow<'_, str>> {
let text = self.text().as_str(); let text = self.text().as_str();
let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
Some(text.to_string()) Some(Cow::Borrowed(text))
} }
} }