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 to clipboard button for code snippets #1454

Closed

Conversation

abhishalya
Copy link
Contributor

@abhishalya abhishalya commented Oct 27, 2020

Preview:

image

Closes #1055

@abhishalya abhishalya force-pushed the abhishalya/clipboard_copy branch 4 times, most recently from d214bbf to f28bdb3 Compare October 27, 2020 13:03
@fredrikekre
Copy link
Member

Nice!

Is it possible to get actual newlines instead of the character \n somehow? This is what I get after copying a codeblock with

1 + 1
2 + 2
julia> 1 + 1\n2 + 2

@abhishalya
Copy link
Contributor Author

Yeah, I'm trying to figure out a way to do it. Is there anything we can do from Julia side? The other option would be to handle via javascript.

@fredrikekre
Copy link
Member

If you generate

<button class="copy-button button fas fa-copy" data-clipboard-text="1 + 1
2 + 2"></button>

instead of

<button class="copy-button button fas fa-copy" data-clipboard-text="1 + 1\n2 + 2"></button>

it seems to work.

@fredrikekre
Copy link
Member

It would also be nice to strip # output from the text in e.g.

```jldoctest
1 + 1
2 + 2

# output

4
```

@mortenpi mortenpi added this to the 0.26.0 milestone Oct 28, 2020
@mortenpi mortenpi added Format: HTML Related to the default HTML output Type: Enhancement labels Oct 28, 2020
@mortenpi
Copy link
Member

For the newlines, I think we should modify the HTML generation in DOM so that newlines in attributes are replaced by &#10;. That way we don't get any weird indentation problems.

It would also be nice to strip # output from the text in e.g.

As long as we render it as a single <pre> block, I think I would find it surprising if the copy-to-clipboard doesn't copy the content exactly.

language = Utilities.codelang(c.language)
language = isempty(language) ? "none" : language
pre(code[".language-$(language)"](c.code))
code_block = [
button[".copy-button .button .fas .fa-copy", Symbol("data-clipboard-text")=>c.code],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be nice to have a title attribute on each button as well that would probably say "Copy to clipboard".

@fredrikekre
Copy link
Member

fredrikekre commented Oct 28, 2020

As long as we render it as a single <pre> block, I think I would find it surprising if the copy-to-clipboard doesn't copy the content exactly.

Valid point, but it kind of defeats the purpose of having the button if the result is not pasteable into a Julia REPL? But perhaps it would be nice to render as two blocks instead, the #output in the final html is not very nice.

@mortenpi
Copy link
Member

But perhaps it would be nice to render as two blocks instead, the #output in the final html is not very nice.

I think that's worth considering (#1455), but let's leave it for a separate PR.

@fredrikekre
Copy link
Member

It would be nice to also show Copied! or similar when you click the button, see e.g. the copy-buttons on https://doc.rust-lang.org/book/ch01-02-hello-world.html

@abhishalya abhishalya force-pushed the abhishalya/clipboard_copy branch 4 times, most recently from 694c73e to 6cae6cd Compare October 29, 2020 14:56
Copy link
Member

@mortenpi mortenpi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a bunch of bikeshedding thoughts:

  1. I think an arrow-pointing-to-clipboard icon (e.g. like they use on the clipboard.js website) would be better, but I couldn't actually find one in FontAwesome, so we can probably stick with the current one.

  2. We could make the button hide itself if you're not hovering over the <pre> box (like we do with the "Source" links on docstrings), so it would not distract when not necessary.

  3. How hard do you think would it be to make the "Copied" text appear next to the button (e.g. in a bubble, like on the clipboard.js site), as opposed to replacing the icon inside the button? It's just the button changing size does not look the prettiest in my opinion.

@@ -0,0 +1,3 @@
.copy-button {
float: right;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should make sure that it's the same distance from top and right side of the box:

Suggested change
float: right;
float: right;
top: -0.2rem;

@mortenpi
Copy link
Member

Oh, one more thing, would you mind adding an entry into the CHANGELOG.md file as well about this PR?

Comment on lines 295 to 297
end
if occursin("\n", text)
replace(text, "\n"=>"&#10;")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should go into the if/for above. Currently it's actually breaking the escaping (the top if never returns from the function).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I realised that. The problem here though is that if I replace it there, the code block doesn't render properly. I believe highlight.js needs those "/n"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. Should be okay if we only escape \n-s in attributes? I think we could implement that by adding a keyword argument to escapehtml and toggling that wherever we're escaping the attribute contents?

@abhishalya
Copy link
Contributor Author

@mortenpi Have a look at this once you're free, I've tried to address as many comments as I could. Do let me know what you think about this :)

Copy link
Member

@mortenpi mortenpi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking really good!

Just one more CSS gripe: it looks like the "Copied" message box ends up being inside the <pre> box, so it's (1) partly cut off, and (2) creates a scrollbar on the code block when it appears:

copied

assets/html/scss/documenter/components/_clipboard.scss Outdated Show resolved Hide resolved
assets/html/scss/documenter/components/_clipboard.scss Outdated Show resolved Hide resolved
assets/html/scss/documenter/components/_clipboard.scss Outdated Show resolved Hide resolved
assets/html/scss/documenter/components/_clipboard.scss Outdated Show resolved Hide resolved
src/Utilities/DOM.jl Outdated Show resolved Hide resolved
src/Utilities/DOM.jl Outdated Show resolved Hide resolved
src/Utilities/DOM.jl Outdated Show resolved Hide resolved
src/Utilities/DOM.jl Outdated Show resolved Hide resolved
CHANGELOG.md Outdated Show resolved Hide resolved
Copy link
Member

@mortenpi mortenpi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@abhishalya I'm really sorry for not reviewing earlier -- I completely missed that you updated the PR.

Besides the escaping thing, the only other issue I can still see is the "Copied" message creating scrollbars, instead of being of above everything. I tried to find a way around it, but I could quite figure it out. It feels like the solution in this post might be applicable.

@@ -281,7 +281,7 @@ When no escaping is needed then the same object is returned, otherwise a new
string is constructed with the characters escaped. The returned object should
always be treated as an immutable copy and compared using `==` rather than `===`.
"""
function escapehtml(text::AbstractString)
function escapehtml(text::AbstractString; escape_newlines=false)
if occursin(r"[<>&'\"]", text)
Copy link
Member

@mortenpi mortenpi Dec 1, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to do something like this

Suggested change
if occursin(r"[<>&'\"]", text)
if occursin(r"[<>&'\"\\n]", text)

because it currently doesn't escape newlines if there are not other special characters:

julia> Documenter.Utilities.DOM.escapehtml("xyz\nAAA", escape_newlines=true)
"xyz\nAAA"

And I think we could avoid the separate if, and just add another char === '\n' into the for loop below?

@mortenpi mortenpi modified the milestones: 0.26.0, 0.27.0 Dec 8, 2020
@mortenpi mortenpi modified the milestones: 0.27.0, 0.28.0 Jun 9, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Format: HTML Related to the default HTML output Type: Enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature Request: copy to clipboard
3 participants