LSP icon indicating copy to clipboard operation
LSP copied to clipboard

The popup window interrupts the prompt document at an incorrect position

Open Jcskz opened this issue 3 years ago • 11 comments

Describe the bug The popup window interrupts the prompt document at an incorrect position

To Reproduce eg

fn main() {
    let string = String::from("hello");
}
  1. move the mouse to the String word.
  2. Pop up prompt box

Actual behavior: The document of the struct String breaks at You can append a

Expected behavior Show full prompt document, Refer to Rust-lang official documentation of String.

Screenshots Screenshots

Environment:

  • OS: macOS 11.0.1
  • Sublime Text version: 4093
  • LSP version: 1.1.0
  • Language servers used: Rust-Analyzer

Console log

 Parse Error: ">char</span>"><code class="highlight"><span style="color: #93a1a1;">char</span></code></a> to a <code class="highlight"><span style="color: #93a1a1;">String</span></code> with the <a href="String::push"><code class="highlight"><span style="color: #93a1a1;">push</span></code></a> method, and
append a <a href="prim@str"><code class="highlight"><span style="color: #93a1a1;">&amp;str</span></code></a> with the <a href="String::push_str"><code class="highlight"><span style="color: #93a1a1;">push_str</span></code></a> method:</p>
<div class="highlight"><pre><span style="color: #859900; font-style: italic;">let</span><span style="color: #93a1a1;"> </span><span style="color: #859900;">mut</span><span style="color: #93a1a1;"> hello </span><span style="color: #859900;">=</span><span style="color: #93a1a1;"> </span><span style="color: #b58900;">String</span><span style="color: #cb4b16;">::</span><span style="color: #93a1a1;">from</span><span style="color: #93a1a1;">(</span><span style="color: #2aa198;">"</span><span style="color: #2aa198;">Hello, </span><span style="color: #2aa198;">"</span><span style="color: #93a1a1;">)</span><span style="color: #93a1a1;">;</span><br><span style="color: #93a1a1;">&nbsp;</span><br><span style="color: #93a1a1;">hello</span><span style="color: #93a1a1;">.</span><span style="color: #268bd2;">push</span><span style="color: #93a1a1;">(</span><span style="color: #2aa198;">'</span><span style="color: #2aa198;">w</span><span style="color: #2aa198;">'</span><span style="color: #93a1a1;">)</span><span style="color: #93a1a1;">;</span><br><span style="color: #93a1a1;">hello</span><span style="color: #93a1a1;">.</span><span style="color: #268bd2;">push_str</span><span style="color: #93a1a1;">(</span><span style="color: #2aa198;">"</span><span style="color: #2aa198;">orld!</span><span style="color: #2aa198;">"</span><span style="color: #93a1a1;">)</span><span style="color: #93a1a1;">;</span><br></pre></div>

Jcskz avatar Nov 23 '20 10:11 Jcskz

I believe the payload for this is:

  {
    "label": "String",
    "filterText": "String",
    "kind": 22,
    "additionalTextEdits": [],
    "textEdit": {
      "newText": "String",
      "range": {
        "start": {
          "character": 4,
          "line": 1
        },
        "end": {
          "character": 5,
          "line": 1
        }
      }
    },
    "insertTextFormat": 1,
    "deprecated": false,
    "documentation": {
      "value": "A UTF-8 encoded, growable string.\n\nThe `String` type is the most common string type that has ownership over the\ncontents of the string. It has a close relationship with its borrowed\ncounterpart, the primitive [`str`].\n\n# Examples\n\nYou can create a `String` from [a literal string][`str`] with [`String::from`]:\n\n[`String::from`]: From::from\n\n```rust\nlet hello = String::from(\"Hello, world!\");\n```\n\nYou can append a [`char`] to a `String` with the [`push`] method, and\nappend a [`&str`] with the [`push_str`] method:\n\n```rust\nlet mut hello = String::from(\"Hello, \");\n\nhello.push('w');\nhello.push_str(\"orld!\");\n```\n\n[`push`]: String::push\n[`push_str`]: String::push_str\n\nIf you have a vector of UTF-8 bytes, you can create a `String` from it with\nthe [`from_utf8`] method:\n\n```rust\n// some bytes, in a vector\nlet sparkle_heart = vec![240, 159, 146, 150];\n\n// We know these bytes are valid, so we'll use `unwrap()`.\nlet sparkle_heart = String::from_utf8(sparkle_heart).unwrap();\n\nassert_eq!(\"\ud83d\udc96\", sparkle_heart);\n```\n\n[`from_utf8`]: String::from_utf8\n\n# UTF-8\n\n`String`s are always valid UTF-8. This has a few implications, the first of\nwhich is that if you need a non-UTF-8 string, consider [`OsString`]. It is\nsimilar, but without the UTF-8 constraint. The second implication is that\nyou cannot index into a `String`:\n\n```compile_fail,E0277\nlet s = \"hello\";\n\nprintln!(\"The first letter of s is {}\", s[0]); // ERROR!!!\n```\n\n[`OsString`]: ../../std/ffi/struct.OsString.html\n\nIndexing is intended to be a constant-time operation, but UTF-8 encoding\ndoes not allow us to do this. Furthermore, it's not clear what sort of\nthing the index should return: a byte, a codepoint, or a grapheme cluster.\nThe [`bytes`] and [`chars`] methods return iterators over the first\ntwo, respectively.\n\n[`bytes`]: str::bytes\n[`chars`]: str::chars\n\n# Deref\n\n`String`s implement [`Deref`]`<Target=str>`, and so inherit all of [`str`]'s\nmethods. In addition, this means that you can pass a `String` to a\nfunction which takes a [`&str`] by using an ampersand (`&`):\n\n```rust\nfn takes_str(s: &str) { }\n\nlet s = String::from(\"Hello\");\n\ntakes_str(&s);\n```\n\nThis will create a [`&str`] from the `String` and pass it in. This\nconversion is very inexpensive, and so generally, functions will accept\n[`&str`]s as arguments unless they need a `String` for some specific\nreason.\n\nIn certain cases Rust doesn't have enough information to make this\nconversion, known as [`Deref`] coercion. In the following example a string\nslice [`&'a str`][`&str`] implements the trait `TraitExample`, and the function\n`example_func` takes anything that implements the trait. In this case Rust\nwould need to make two implicit conversions, which Rust doesn't have the\nmeans to do. For that reason, the following example will not compile.\n\n```compile_fail,E0277\ntrait TraitExample {}\n\nimpl<'a> TraitExample for &'a str {}\n\nfn example_func<A: TraitExample>(example_arg: A) {}\n\nlet example_string = String::from(\"example_string\");\nexample_func(&example_string);\n```\n\nThere are two options that would work instead. The first would be to\nchange the line `example_func(&example_string);` to\n`example_func(example_string.as_str());`, using the method [`as_str()`]\nto explicitly extract the string slice containing the string. The second\nway changes `example_func(&example_string);` to\n`example_func(&*example_string);`. In this case we are dereferencing a\n`String` to a [`str`][`&str`], then referencing the [`str`][`&str`] back to\n[`&str`]. The second way is more idiomatic, however both work to do the\nconversion explicitly rather than relying on the implicit conversion.\n\n# Representation\n\nA `String` is made up of three components: a pointer to some bytes, a\nlength, and a capacity. The pointer points to an internal buffer `String`\nuses to store its data. The length is the number of bytes currently stored\nin the buffer, and the capacity is the size of the buffer in bytes. As such,\nthe length will always be less than or equal to the capacity.\n\nThis buffer is always stored on the heap.\n\nYou can look at these with the [`as_ptr`], [`len`], and [`capacity`]\nmethods:\n\n```rust\nuse std::mem;\n\nlet story = String::from(\"Once upon a time...\");\n\n// Prevent automatically dropping the String's data\nlet mut story = mem::ManuallyDrop::new(story);\n\nlet ptr = story.as_mut_ptr();\nlet len = story.len();\nlet capacity = story.capacity();\n\n// story has nineteen bytes\nassert_eq!(19, len);\n\n// We can re-build a String out of ptr, len, and capacity. This is all\n// unsafe because we are responsible for making sure the components are\n// valid:\nlet s = unsafe { String::from_raw_parts(ptr, len, capacity) } ;\n\nassert_eq!(String::from(\"Once upon a time...\"), s);\n```\n\n[`as_ptr`]: str::as_ptr\n[`len`]: String::len\n[`capacity`]: String::capacity\n\nIf a `String` has enough capacity, adding elements to it will not\nre-allocate. For example, consider this program:\n\n```rust\nlet mut s = String::new();\n\nprintln!(\"{}\", s.capacity());\n\nfor _ in 0..5 {\n    s.push_str(\"hello\");\n    println!(\"{}\", s.capacity());\n}\n```\n\nThis will output the following:\n\n```text\n0\n5\n10\n20\n20\n40\n```\n\nAt first, we have no memory allocated at all, but as we append to the\nstring, it increases its capacity appropriately. If we instead use the\n[`with_capacity`] method to allocate the correct capacity initially:\n\n```rust\nlet mut s = String::with_capacity(25);\n\nprintln!(\"{}\", s.capacity());\n\nfor _ in 0..5 {\n    s.push_str(\"hello\");\n    println!(\"{}\", s.capacity());\n}\n```\n\n[`with_capacity`]: String::with_capacity\n\nWe end up with a different output:\n\n```text\n25\n25\n25\n25\n25\n25\n```\n\nHere, there's no need to allocate more memory inside the loop.\n\n[`str`]: prim@str\n[`&str`]: prim@str\n[`Deref`]: core::ops::Deref\n[`as_str()`]: String::as_str",
      "kind": "markdown"
    }
  },

This payload is from completion rather than the hover request but hopefully, it triggers the same issue.

rchl avatar Nov 23 '20 10:11 rchl

It may be due to <a href="prim@str"> ?

rwols avatar Nov 23 '20 10:11 rwols

I cannot reproduce it.

Here is the log:

:: --> rust-analyzer textDocument/hover(54): {'position': {'character': 20, 'line': 1}, 'textDocument': {'uri': 'file:///home/predrag/Documents/sandbox/rust/app/src/main.rs'}}
:: <<< rust-analyzer 54: {'contents': {'value': '\n```rust\nalloc::string\n```\n\n```rust\npub struct String\n```\n\n---\n\nA UTF-8 encoded, growable string.\n\nThe `String` type is the most common string type that has ownership over the\ncontents of the string. It has a close relationship with its borrowed\ncounterpart, the primitive [`str`](https://doc.rust-lang.org/nightly/std/primitive.str.html).\n\n# Examples\n\nYou can create a `String` from a literal string with [`String::from`](https://doc.rust-lang.org/nightly/alloc/string/struct.String.html#method.from):\n\n```rust\nlet hello = String::from("Hello, world!");\n```\n\nYou can append a [`char`](https://doc.rust-lang.org/nightly/std/primitive.char.html) to a `String` with the [`push`](https://doc.rust-lang.org/nightly/alloc/string/struct.String.html#method.push) method, and\nappend a [`&str`](https://doc.rust-lang.org/nightly/std/primitive.str.html) with the [`push_str`](https://doc.rust-lang.org/nightly/alloc/string/struct.String.html#method.push_str) method:\n\n```rust\nlet mut hello = String::from("Hello, ");\n\nhello.push(\'w\');\nhello.push_str("orld!");\n```\n\nIf you have a vector of UTF-8 bytes, you can create a `String` from it with\nthe [`from_utf8`](https://doc.rust-lang.org/nightly/alloc/string/struct.String.html#method.from_utf8) method:\n\n```rust\n// some bytes, in a vector\nlet sparkle_heart = vec![240, 159, 146, 150];\n\n// We know these bytes are valid, so we\'ll use `unwrap()`.\nlet sparkle_heart = String::from_utf8(sparkle_heart).unwrap();\n\nassert_eq!("💖", sparkle_heart);\n```\n\n# UTF-8\n\n`String`s are always valid UTF-8. This has a few implications, the first of\nwhich is that if you need a non-UTF-8 string, consider [`OsString`](https://doc.rust-lang.org/nightly/std/ffi/struct.OsString.html). It is\nsimilar, but without the UTF-8 constraint. The second implication is that\nyou cannot index into a `String`:\n\n```compile_fail,E0277\nlet s = "hello";\n\nprintln!("The first letter of s is {}", s[0]); // ERROR!!!\n```\n\nIndexing is intended to be a constant-time operation, but UTF-8 encoding\ndoes not allow us to do this. Furthermore, it\'s not clear what sort of\nthing the index should return: a byte, a codepoint, or a grapheme cluster.\nThe [`bytes`](https://doc.rust-lang.org/nightly/alloc/string/struct.String.html#method.bytes) and [`chars`](https://doc.rust-lang.org/nightly/alloc/string/struct.String.html#method.chars) methods return iterators over the first\ntwo, respectively.\n\n# Deref\n\n`String`s implement [`Deref`](https://doc.rust-lang.org/nightly/std/ops/trait.Deref.html)`<Target=str>`, and so inherit all of [`str`](https://doc.rust-lang.org/nightly/std/primitive.str.html)\'s\nmethods. In addition, this means that you can pass a `String` to a\nfunction which takes a [`&str`](https://doc.rust-lang.org/nightly/std/primitive.str.html) by using an ampersand (`&`):\n\n```rust\nfn takes_str(s: &str) { }\n\nlet s = String::from("Hello");\n\ntakes_str(&s);\n```\n\nThis will create a [`&str`](https://doc.rust-lang.org/nightly/std/primitive.str.html) from the `String` and pass it in. This\nconversion is very inexpensive, and so generally, functions will accept\n[`&str`](https://doc.rust-lang.org/nightly/std/primitive.str.html)s as arguments unless they need a `String` for some specific\nreason.\n\nIn certain cases Rust doesn\'t have enough information to make this\nconversion, known as [`Deref`](https://doc.rust-lang.org/nightly/std/ops/trait.Deref.html) coercion. In the following example a string\nslice [`&\'a str`](https://doc.rust-lang.org/nightly/std/primitive.str.html) implements the trait `TraitExample`, and the function\n`example_func` takes anything that implements the trait. In this case Rust\nwould need to make two implicit conversions, which Rust doesn\'t have the\nmeans to do. For that reason, the following example will not compile.\n\n```compile_fail,E0277\ntrait TraitExample {}\n\nimpl<\'a> TraitExample for &\'a str {}\n\nfn example_func<A: TraitExample>(example_arg: A) {}\n\nlet example_string = String::from("example_string");\nexample_func(&example_string);\n```\n\nThere are two options that would work instead. The first would be to\nchange the line `example_func(&example_string);` to\n`example_func(example_string.as_str());`, using the method [`as_str()`](https://doc.rust-lang.org/nightly/alloc/string/struct.String.html#method.as_str)\nto explicitly extract the string slice containing the string. The second\nway changes `example_func(&example_string);` to\n`example_func(&*example_string);`. In this case we are dereferencing a\n`String` to a [`str`](https://doc.rust-lang.org/nightly/std/primitive.str.html), then referencing the [`str`](https://doc.rust-lang.org/nightly/std/primitive.str.html) back to\n[`&str`](https://doc.rust-lang.org/nightly/std/primitive.str.html). The second way is more idiomatic, however both work to do the\nconversion explicitly rather than relying on the implicit conversion.\n\n# Representation\n\nA `String` is made up of three components: a pointer to some bytes, a\nlength, and a capacity. The pointer points to an internal buffer `String`\nuses to store its data. The length is the number of bytes currently stored\nin the buffer, and the capacity is the size of the buffer in bytes. As such,\nthe length will always be less than or equal to the capacity.\n\nThis buffer is always stored on the heap.\n\nYou can look at these with the [`as_ptr`](https://doc.rust-lang.org/nightly/alloc/string/struct.String.html#method.as_ptr), [`len`](https://doc.rust-lang.org/nightly/alloc/string/struct.String.html#method.len), and [`capacity`](https://doc.rust-lang.org/nightly/alloc/string/struct.String.html#method.capacity)\nmethods:\n\n```rust\nuse std::mem;\n\nlet story = String::from("Once upon a time...");\n\n// Prevent automatically dropping the String\'s data\nlet mut story = mem::ManuallyDrop::new(story);\n\nlet ptr = story.as_mut_ptr();\nlet len = story.len();\nlet capacity = story.capacity();\n\n// story has nineteen bytes\nassert_eq!(19, len);\n\n// We can re-build a String out of ptr, len, and capacity. This is all\n// unsafe because we are responsible for making sure the components are\n// valid:\nlet s = unsafe { String::from_raw_parts(ptr, len, capacity) } ;\n\nassert_eq!(String::from("Once upon a time..."), s);\n```\n\nIf a `String` has enough capacity, adding elements to it will not\nre-allocate. For example, consider this program:\n\n```rust\nlet mut s = String::new();\n\nprintln!("{}", s.capacity());\n\nfor _ in 0..5 {\n    s.push_str("hello");\n    println!("{}", s.capacity());\n}\n```\n\nThis will output the following:\n\n```text\n0\n5\n10\n20\n20\n40\n```\n\nAt first, we have no memory allocated at all, but as we append to the\nstring, it increases its capacity appropriately. If we instead use the\n[`with_capacity`](https://doc.rust-lang.org/nightly/alloc/string/struct.String.html#method.with_capacity) method to allocate the correct capacity initially:\n\n```rust\nlet mut s = String::with_capacity(25);\n\nprintln!("{}", s.capacity());\n\nfor _ in 0..5 {\n    s.push_str("hello");\n    println!("{}", s.capacity());\n}\n```\n\nWe end up with a different output:\n\n```text\n25\n25\n25\n25\n25\n25\n```\n\nHere, there\'s no need to allocate more memory inside the loop.', 'kind': 'markdown'}, 'range': {'end': {'character': 24, 'line': 1}, 'start': {'character': 18, 'line': 1}}}

old

predragnikolic avatar Nov 23 '20 13:11 predragnikolic

Although the parsing does seem to break at the fn example_func part in the payload I attached above:

image

But I don't see any parse errors in ST console.

predragnikolic avatar Nov 23 '20 13:11 predragnikolic

@Jcskz can you post the log with the textDocument/hover response?

rchl avatar Nov 23 '20 13:11 rchl

ServerLog.log

Jcskz avatar Nov 24 '20 01:11 Jcskz

Well, that content triggers the #1473 issue so we have to wait for the next ST build and then re-test this one. I suppose it might just work then (maybe besides the issue that @predragnikolic mentioned as that's possibly a server issue).

rchl avatar Nov 24 '20 08:11 rchl

Although the parsing does seem to break at the fn example_func part in the payload I attached above:

That is yet another issue where mdpopups doesn't parse the markdown language ID :) it does look like a silly markdown language ID.

rwols avatar Nov 27 '20 20:11 rwols

I believe that the code block is specified to accept a language identifier and then optionally an info string data after a white space. And this doesn't look anything like that:

```compile_fail,E0277

And the text itself is probably a good indication that the server just failed here.

rchl avatar Nov 27 '20 21:11 rchl

https://github.com/rust-analyzer/rust-analyzer/issues/6655

rwols avatar Nov 27 '20 21:11 rwols

I cannot reproduce the cutoff with rust-analyzer version 2020-11-30 & LSP 1.2.2 & ST 4094.

That said, the problem of the weird markdown language IDs remains. But I consider that a server issue.

rwols avatar Dec 03 '20 20:12 rwols

Cannot reproduce this with LSP-rust-analyzer (tag 2022-08-29).

:: <<< rust-analyzer 21: {'contents': {'value': '\n```rust\nalloc::string\n```\n\n```rust\npub struct String\n```\n\n---\n\nA UTF-8–encoded, growable string.\n\nThe `String` type is the most common string type that has ownership over the\ncontents of the string. It has a close relationship with its borrowed\ncounterpart, the primitive [`str`].\n\n# Examples\n\nYou can create a `String` from [a literal string](https://doc.rust-lang.org/nightly/alloc/str/index.html) with [`String::from`]:\n\n```rust\nlet hello = String::from("Hello, world!");\n```\n\nYou can append a [`char`](https://doc.rust-lang.org/nightly/core/primitive.char.html) to a `String` with the [`push`] method, and\nappend a [`&str`] with the [`push_str`] method:\n\n```rust\nlet mut hello = String::from("Hello, ");\n\nhello.push(\'w\');\nhello.push_str("orld!");\n```\n\nIf you have a vector of UTF-8 bytes, you can create a `String` from it with\nthe [`from_utf8`] method:\n\n```rust\n// some bytes, in a vector\nlet sparkle_heart = vec![240, 159, 146, 150];\n\n// We know these bytes are valid, so we\'ll use `unwrap()`.\nlet sparkle_heart = String::from_utf8(sparkle_heart).unwrap();\n\nassert_eq!("💖", sparkle_heart);\n```\n\n# UTF-8\n\n`String`s are always valid UTF-8. This has a few implications, the first of\nwhich is that if you need a non-UTF-8 string, consider [`OsString`](https://doc.rust-lang.org/nightly/std/ffi/struct.OsString.html). It is\nsimilar, but without the UTF-8 constraint. The second implication is that\nyou cannot index into a `String`:\n\n```rust\nlet s = "hello";\n\nprintln!("The first letter of s is {}", s[0]); // ERROR!!!\n```\n\nIndexing is intended to be a constant-time operation, but UTF-8 encoding\ndoes not allow us to do this. Furthermore, it\'s not clear what sort of\nthing the index should return: a byte, a codepoint, or a grapheme cluster.\nThe [`bytes`] and [`chars`] methods return iterators over the first\ntwo, respectively.\n\n# Deref\n\n`String` implements <code>\n[Deref]\\<Target = [str]\\></code>, and so inherits all of [`str`]\'s\nmethods. In addition, this means that you can pass a `String` to a\nfunction which takes a [`&str`] by using an ampersand (`&`):\n\n```rust\nfn takes_str(s: &str) { }\n\nlet s = String::from("Hello");\n\ntakes_str(&s);\n```\n\nThis will create a [`&str`] from the `String` and pass it in. This\nconversion is very inexpensive, and so generally, functions will accept\n[`&str`]s as arguments unless they need a `String` for some specific\nreason.\n\nIn certain cases Rust doesn\'t have enough information to make this\nconversion, known as [`Deref`] coercion. In the following example a string\nslice [`&\'a str`](https://doc.rust-lang.org/nightly/alloc/str/index.html) implements the trait `TraitExample`, and the function\n`example_func` takes anything that implements the trait. In this case Rust\nwould need to make two implicit conversions, which Rust doesn\'t have the\nmeans to do. For that reason, the following example will not compile.\n\n```rust\ntrait TraitExample {}\n\nimpl<\'a> TraitExample for &\'a str {}\n\nfn example_func<A: TraitExample>(example_arg: A) {}\n\nlet example_string = String::from("example_string");\nexample_func(&example_string);\n```\n\nThere are two options that would work instead. The first would be to\nchange the line `example_func(&example_string);` to\n`example_func(example_string.as_str());`, using the method [`as_str()`]\nto explicitly extract the string slice containing the string. The second\nway changes `example_func(&example_string);` to\n`example_func(&*example_string);`. In this case we are dereferencing a\n`String` to a [`str`], then referencing the [`str`] back to\n[`&str`]. The second way is more idiomatic, however both work to do the\nconversion explicitly rather than relying on the implicit conversion.\n\n# Representation\n\nA `String` is made up of three components: a pointer to some bytes, a\nlength, and a capacity. The pointer points to an internal buffer `String`\nuses to store its data. The length is the number of bytes currently stored\nin the buffer, and the capacity is the size of the buffer in bytes. As such,\nthe length will always be less than or equal to the capacity.\n\nThis buffer is always stored on the heap.\n\nYou can look at these with the [`as_ptr`], [`len`], and [`capacity`]\nmethods:\n\n```rust\nuse std::mem;\n\nlet story = String::from("Once upon a time...");\n\n// Prevent automatically dropping the String\'s data\nlet mut story = mem::ManuallyDrop::new(story);\n\nlet ptr = story.as_mut_ptr();\nlet len = story.len();\nlet capacity = story.capacity();\n\n// story has nineteen bytes\nassert_eq!(19, len);\n\n// We can re-build a String out of ptr, len, and capacity. This is all\n// unsafe because we are responsible for making sure the components are\n// valid:\nlet s = unsafe { String::from_raw_parts(ptr, len, capacity) } ;\n\nassert_eq!(String::from("Once upon a time..."), s);\n```\n\nIf a `String` has enough capacity, adding elements to it will not\nre-allocate. For example, consider this program:\n\n```rust\nlet mut s = String::new();\n\nprintln!("{}", s.capacity());\n\nfor _ in 0..5 {\n    s.push_str("hello");\n    println!("{}", s.capacity());\n}\n```\n\nThis will output the following:\n\n```text\n0\n5\n10\n20\n20\n40\n```\n\nAt first, we have no memory allocated at all, but as we append to the\nstring, it increases its capacity appropriately. If we instead use the\n[`with_capacity`] method to allocate the correct capacity initially:\n\n```rust\nlet mut s = String::with_capacity(25);\n\nprintln!("{}", s.capacity());\n\nfor _ in 0..5 {\n    s.push_str("hello");\n    println!("{}", s.capacity());\n}\n```\n\nWe end up with a different output:\n\n```text\n25\n25\n25\n25\n25\n25\n```\n\nHere, there\'s no need to allocate more memory inside the loop.', 'kind': 'markdown'}, 'range': {'end': {'line': 5, 'character': 24}, 'start': {'line': 5, 'character': 18}}}

I managed to scroll to the bottom of the hover popup :)

image

predragnikolic avatar Sep 27 '22 11:09 predragnikolic