Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add copy button for code blocks #86851

Closed
GuillaumeGomez opened this issue Jul 3, 2021 · 11 comments · Fixed by #125779
Closed

Add copy button for code blocks #86851

GuillaumeGomez opened this issue Jul 3, 2021 · 11 comments · Fixed by #125779
Assignees
Labels
A-rustdoc-js Area: Rustdoc's JS front-end A-rustdoc-ui Area: Rustdoc UI (generated HTML) T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue.

Comments

@GuillaumeGomez
Copy link
Member

GuillaumeGomez commented Jul 3, 2021

As discussed in #86363, we could add a button allowing to copy the content of the source code. For this, I'll unify the content for the "run" button as well so it's not duplicated in case the run button is there as well. Or we could simply generate the button content when clicking on it using JS (because the "copy to clipboard" can only be done with JS), meaning we also generate the hidden lines (but keep them invisible).

Second point: for now the "run" button is always visible but its opacity goes to 1 when you hover it. I'll change this behaviour by hiding those buttons and only making them appear when hovering the code example so it improves the reading experience. However, I'll keep the opacity change behaviour when going on top of the button.

Last thing: should we add this copy button to all code blocks or only the rust ones?

cc @rust-lang/rustdoc

@GuillaumeGomez GuillaumeGomez added T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue. A-rustdoc-ui Area: Rustdoc UI (generated HTML) A-rustdoc-js Area: Rustdoc's JS front-end labels Jul 3, 2021
@GuillaumeGomez GuillaumeGomez self-assigned this Jul 3, 2021
@Manishearth
Copy link
Member

So i think having just a regular copy button for copying the examples as shown would be nice. However I think in #86363 folks were discussing this as a way to show the "hidden" code that's in examples to make them run -- I don't think a "copy" button should ever copy text that it is not being displayed.

I do think if we want to make the hidden text visible we should have an "expand" button that shows greyed-out code. The copy button could still copy the hidden text when in expanded mode.

@jsha
Copy link
Contributor

jsha commented Jul 5, 2021

Agreed that we should only copy visible text. Here's an idea: when you click the "copy" button it shows "Copied ✓. To reveal and copy hidden boilerplate, click here." And it could show the latter part only when there actually is hidden boilerplate.

@Manishearth
Copy link
Member

Would be weird to only expose the "reveal hidden boilerplate" option when you click copy, I'm thinking adding a second button with an "expand" icon on it

@GuillaumeGomez
Copy link
Member Author

I can do that as well. I already have the code ready for the "copy to clipboard" so I can add an expand button as well.

@camelid
Copy link
Member

camelid commented Jul 5, 2021

I read through this thread and the linked playground-url PR, but I wasn't able to find a description of why the copy and expand features would be useful. I would like to understand better why people want this feature. I feel a bit concerned because the buttons add some visual weight to the docs, and as we've discussed before, the docs already have a lot of visual components that make them harder to read. For me personally, I have very rarely wanted to copy and paste an example or see the hidden code. My understanding is that hidden code is usually an "implementation detail" of the example, and shouldn't be displayed to the reader.

I'm open to the idea, I would just like to understand where people are coming from :)

@GuillaumeGomez
Copy link
Member Author

GuillaumeGomez commented Jul 6, 2021

I originally wanted to add the "run" button in every case to be able to have all the code so I can edit it (fully, not just the visible part). This is often an issue when copying/pasting code which lacks the "implementation details" because then you have to look again what's missing, mostly in cases where ? is used. Therefore it was suggested to simply add a button to copy it and another one to expand/collapse the code block.

As for the visual weight, I intend to make all the code blocks' buttons hidden by default until you hover the code block. That should reduce the visual weight quite a lot.

@jsha
Copy link
Contributor

jsha commented Sep 30, 2021

Bringing over part of @camelid's comment

Part of why I don't understand the use case is that I find that most code examples I look at can't just be copied and pasted into my code; only a small piece, say a function call, is relevant, and that I can type myself. For example, with Iterator::filter(), most of the examples are just setup and assertions to demonstrate the function in context. Nothing except .iter().filter() would be relevant to my code, so I wouldn't copy-and-paste.

Here are a couple of counterexamples from popular crates, of code you might want to copy-paste and then modify:

https://docs.rs/log/0.4.14/log/#implementing-a-logger

use log::{Record, Level, Metadata};

struct SimpleLogger;

impl log::Log for SimpleLogger {
    fn enabled(&self, metadata: &Metadata) -> bool {
        metadata.level() <= Level::Info
    }

    fn log(&self, record: &Record) {
        if self.enabled(record.metadata()) {
            println!("{} - {}", record.level(), record.args());
        }
    }

    fn flush(&self) {}
}

https://doc.rust-lang.org/std/net/struct.TcpStream.html#examples

use std::io::prelude::*;
use std::net::TcpStream;

fn main() -> std::io::Result<()> {
    let mut stream = TcpStream::connect("127.0.0.1:34254")?;

    stream.write(&[1])?;
    stream.read(&mut [0; 128])?;
    Ok(())
} // the stream is closed here

https://docs.serde.rs/serde_json/#creating-json-by-serializing-data-structures

#[derive(Serialize, Deserialize)]
struct Address {
    street: String,
    city: String,
}

fn print_an_address() -> Result<()> {
    // Some data structure.
    let address = Address {
        street: "10 Downing Street".to_owned(),
        city: "London".to_owned(),
    };

    // Serialize it to a JSON string.
    let j = serde_json::to_string(&address)?;

    // Print, write to a file, or send to an HTTP server.
    println!("{}", j);

    Ok(())
}

That said, browsers do provide built-in functionality to copy-paste, so the question is: does adding a "copy" button to each code block make copying easier by enough of a margin to be worth the screen real estate?

@GuillaumeGomez, one thing that might help resolve some of the team's ambivalence about the feature would be to talk to some other people who have a need for this feature. Do you know other folks who expect to find it useful?

@GuillaumeGomez
Copy link
Member Author

Mostly people I talked to directly when we met. We can always propose a poll and put the link on twitter, reddit and the rust forums?

@Manishearth
Copy link
Member

I don't actually think that'll work. When you ask most people "what do you think of feature X", you'll get a response of either excitement or ambivalence, folks are not often against new features, which skews the dataset towards people who want it. OTOH as a product team we have to be careful to not just randomly add features; we need to make sure we're filling a need and balance that against our ability to maintain things.

As I said here, we need to focus on the actual problems people are facing without getting ahead of ourselves with solutions. If we're going to be surveying users, we should ask around for what kind of challenges people face in this area, if at all, and keep in mind that the only people who respond will be ones with challenges.

@jsha
Copy link
Contributor

jsha commented Oct 6, 2021

Here's my attempt at it:

Problem 1: As a new user, if I copy an example from a crate's documentation on docs.rs, sometimes it doesn't compile because it uses ?, but the fn main() -> Result<_> { wrapper is hidden, and I don't know I need to add it.

Problem 2: As an experienced user, if I copy an example from a crate's documentation, sometimes it doesn't compile because it relies on hidden use statements, and it's hard to find out which use statements I need to write to make it work.

Problem 3: As an intermediate user, if I copy an example, sometimes it doesn't compile because it relies on hidden #[allow(unused)] or #[allow(dead_code)].

@Manishearth
Copy link
Member

Thanks, but stepping back I mean to say that if we're going to be surveying users we should figure out if these are problems they face, and how.

GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this issue Jul 28, 2024
…c-team

[rustdoc] Add copy code feature

This PR adds a "copy code" to code blocks. Since this is a JS only feature, the HTML is generated with JS when the user hovers the code block to prevent generating DOM unless needed.

Two things to note:
 1. I voluntarily kept the current behaviour of the run button (only when hovering a code block with a mouse) so it doesn't do anything on mobile. I plan to send a follow-up where the buttons would "expandable" or something. Still need to think which approach would be the best.
 2. I used a picture and not text like the run button to remain consistent with the "copy path" button. I'd also prefer for the run button to use a picture (like what is used in mdbook) but again, that's something to be discussed later on.

The rendering looks like this:

![Screenshot from 2024-06-03 21-29-48](https://github.com/rust-lang/rust/assets/3050060/a0b18f9c-b3dd-4a65-89a7-5a7a303b5c2b)
![Screenshot from 2024-06-03 21-30-20](https://github.com/rust-lang/rust/assets/3050060/b3b084ff-2716-4160-820b-d4774681a961)

It can be tested [here](https://guillaume-gomez.fr/rustdoc/bar/struct.Bar.html) (without the run button) and [here](https://guillaume-gomez.fr/rustdoc/foo/struct.Bar.html) (with the run button).

Fixes rust-lang#86851.

r? `@notriddle`
@bors bors closed this as completed in a8cc24a Jul 28, 2024
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Jul 28, 2024
Rollup merge of rust-lang#125779 - GuillaumeGomez:copy-code, r=rustdoc-team

[rustdoc] Add copy code feature

This PR adds a "copy code" to code blocks. Since this is a JS only feature, the HTML is generated with JS when the user hovers the code block to prevent generating DOM unless needed.

Two things to note:
 1. I voluntarily kept the current behaviour of the run button (only when hovering a code block with a mouse) so it doesn't do anything on mobile. I plan to send a follow-up where the buttons would "expandable" or something. Still need to think which approach would be the best.
 2. I used a picture and not text like the run button to remain consistent with the "copy path" button. I'd also prefer for the run button to use a picture (like what is used in mdbook) but again, that's something to be discussed later on.

The rendering looks like this:

![Screenshot from 2024-06-03 21-29-48](https://github.com/rust-lang/rust/assets/3050060/a0b18f9c-b3dd-4a65-89a7-5a7a303b5c2b)
![Screenshot from 2024-06-03 21-30-20](https://github.com/rust-lang/rust/assets/3050060/b3b084ff-2716-4160-820b-d4774681a961)

It can be tested [here](https://guillaume-gomez.fr/rustdoc/bar/struct.Bar.html) (without the run button) and [here](https://guillaume-gomez.fr/rustdoc/foo/struct.Bar.html) (with the run button).

Fixes rust-lang#86851.

r? ``@notriddle``
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-rustdoc-js Area: Rustdoc's JS front-end A-rustdoc-ui Area: Rustdoc UI (generated HTML) T-rustdoc Relevant to the rustdoc team, which will review and decide on the PR/issue.
Projects
None yet
4 participants