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::{
ast::{self, HasStringValue},
ast::{self, HasQuotes, HasStringValue},
AstToken,
SyntaxKind::{RAW_STRING, STRING},
TextSize,
@ -32,14 +34,17 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext) -> Option<
target,
|edit| {
let max_hash_streak = count_hashes(&value);
let mut hashes = String::with_capacity(max_hash_streak + 1);
for _ in 0..hashes.capacity() {
hashes.push('#');
let hashes = "#".repeat(max_hash_streak + 1);
if matches!(value, Cow::Borrowed(_)) {
// 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(
token.syntax().text_range(),
format!("r{}\"{}\"{}", hashes, value, hashes),
);
}
edit.replace(
token.syntax().text_range(),
format!("r{}\"{}\"{}", hashes, value, hashes),
);
},
)
}
@ -70,6 +75,14 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext) -> Optio
|edit| {
// parse inside string to escape `"`
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));
},
)

View file

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

View file

@ -25,7 +25,7 @@ pub(super) fn highlight_injection(
return None;
}
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() {
acc.add(HighlightedRange {

View file

@ -1,6 +1,9 @@
//! 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::{
ast::{AstToken, Comment, RawString, String, Whitespace},
@ -138,11 +141,11 @@ impl HasQuotes for String {}
impl HasQuotes for RawString {}
pub trait HasStringValue: HasQuotes {
fn value(&self) -> Option<std::string::String>;
fn value(&self) -> Option<Cow<'_, str>>;
}
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 = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
@ -156,15 +159,17 @@ impl HasStringValue for String {
if has_error {
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 {
fn value(&self) -> Option<std::string::String> {
fn value(&self) -> Option<Cow<'_, str>> {
let text = self.text().as_str();
let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
Some(text.to_string())
Some(Cow::Borrowed(text))
}
}