Skip to content

Add copy button to code examples #154

Closed

Description

@churnikov

Hi!

I'm planning to move this work to this repo.

@AA-Turner Suggested to me in this PR python/cpython#107037 (comment) to do that. Please tell me, what do you think :)
Do you agree that that's a good idea?

If so, I guess I would need to rewrite this function, right?
https://github.com/python/python-docs-theme/blob/main/python_docs_theme/static/copybutton.js

Activity

  1. AA-Turner commented on Aug 30, 2023

    @AA-Turner
    Member

    We'd probably want to evaluate what exists in the literature thus far; it might be that the copybutton.js that I rewrote is redundant. It functions to hide certain Pygments classes denoting output from interactive console display blocks.

    Should we decide to add a "click to copy" button, I think the functionality should be merged with the existing button.

    A

  2. churnikov commented on Aug 30, 2023

    @churnikov
    Author

    May I ask you, what do you mean by "We'd probably want to evaluate what exists in the literature thus far;"? :)

  3. AA-Turner commented on Aug 30, 2023

    @AA-Turner
    Member

    E.g. https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Interact_with_the_clipboard; https://clipboardjs.com/ -- there are many ways to and libraries for interacting with the clipboard -- ought we abandon our JavaScript, ought we adopt a pre-existing library, ought we use the browser APIs, etc.

    A

  4. ferdnyc commented on Jan 26, 2024

    @ferdnyc

    FYI, there's also a pre-made sphinx_copybutton extension available, that uses clipboard.js.

    (Which, btw, can):

    1. Automatically remove prompts from the copied lines:

      copybutton_prompt_text = r">>> |\.\.\. "
      copybutton_prompt_is_regexp = True
      copybutton_remove_prompts = True  # (default)
    2. Copy only lines with prompts, when it finds prompts:

      copybutton_only_copy_prompt_lines = True  # (default)
    3. Copy multiple wrapped lines and re-assemble them, if the first one starts with a prompt:

      copybutton_line_continuation_character = "\\"

    Full docs: https://sphinx-copybutton.readthedocs.io/

  5. changed the title [-]Proposal to add copy button to code examples [copy of gh-107024][/-] [+]Proposal to add copy button to code examples[/+] on Apr 3, 2025
  6. changed the title [-]Proposal to add copy button to code examples[/-] [+]Add copy button to code examples[/+] on Apr 3, 2025
  7. tomasr8 commented on Apr 5, 2025

    @tomasr8
    Member

    If there's still interest in having this feature, I can give it a go. I would suggest to use navigator.clipboard which has been supported by all major browsers for >5 years at this point.

    We also already have a function which can strip the prompt markers from the code so the implementation (bar the UI) should be relatively simple.

  8. ferdnyc commented on Apr 5, 2025

    @ferdnyc

    @tomasr8 That function also removes the output lines from interactive sessions, which I think is an important part of being able to copy/paste commands from those sessions.

    If I might be permitted a bit of feature creep, I notice that the prompt characters in the listings are styled user-select: none so that they don't get caught up in manual copy actions.

    Rather than having the "strip out the prompts and output" button, it might be easier to just style the output as non-selectable as well, so that even manual selects would only get the input lines. (The copy button could then copy only the same elements, automatically.)

    It's not too useful if selecting and copying this:

    >>> 2 + 2
    4
    >>> 50 - 5*6
    20
    >>> (50 - 5*6) / 4
    5.0
    >>> 8 / 5  # division always returns a floating-point number
    1.6

    gets you this on the clipboard:

    2 + 2
    4
    50 - 5*6
    20
    (50 - 5*6) / 4
    5.0
    8 / 5  # division always returns a floating-point number
    1.6

    ...Because without the prompts, that's completely confusing. Almost better to leave the prompts IN, than to end up with that. But better still, seems to me, if it gets you this:

    2 + 2
    
    50 - 5*6
    
    (50 - 5*6) / 4
    
    8 / 5  # division always returns a floating-point number

    ...which is what happens if you copy after activating the "remove prompts and output" button, but could just as easily happen automatically right from the start.

  9. ferdnyc commented on Apr 5, 2025

    @ferdnyc

    (And the only thing required for things to work that way is adding div.highlight span.go to the user-select: none style rules already in place for div.highlight span.gp and others.)

  10. tomasr8 commented on Apr 5, 2025

    @tomasr8
    Member

    (And the only thing required for things to work that way is adding div.highlight span.go to the user-select: none style rules already in place for div.highlight span.gp and others.)

    One other place which might not be so simple is traceback text which does not have any easily selectable classes (it's currently handled in getHideableCopyButtonElements as well).

  11. StanFromIreland commented on Apr 18, 2025

    @StanFromIreland
    Member

    Currently it hides comments which is not ideal IMO

    Image

    Image

  12. hugovk commented on Apr 18, 2025

    @hugovk
    Member

    How do you mean?

    With #231, clicking copy at https://python-docs-theme-previews--231.org.readthedocs.build/en/231/library/locale.html#locale.CHAR_MAX gives:

    import locale
    loc = locale.getlocale()  # get current locale
    
    locale.setlocale(locale.LC_ALL, 'de_DE')
    locale.strcoll('f\xe4n', 'foo')  # compare a string containing an umlaut
    locale.setlocale(locale.LC_ALL, '')   # use user's preferred locale
    locale.setlocale(locale.LC_ALL, 'C')  # use default (C) locale
    locale.setlocale(locale.LC_ALL, loc)  # restore saved locale

    Which includes all input code, and input comments.

    It doesn't include any output, including comments shown as comments, which looks correct to me.

  13. StanFromIreland commented on Apr 18, 2025

    @StanFromIreland
    Member

    Should the comments not be preserved? They are often quite important in an example.

  14. hugovk commented on Apr 18, 2025

    @hugovk
    Member

    The comments are preserved.

    This is the snippet:

    >>> import locale
    >>> loc = locale.getlocale()  # get current locale
    # use German locale; name might vary with platform
    >>> locale.setlocale(locale.LC_ALL, 'de_DE')
    >>> locale.strcoll('f\xe4n', 'foo')  # compare a string containing an umlaut
    >>> locale.setlocale(locale.LC_ALL, '')   # use user's preferred locale
    >>> locale.setlocale(locale.LC_ALL, 'C')  # use default (C) locale
    >>> locale.setlocale(locale.LC_ALL, loc)  # restore saved locale

    For example, this is a line of REPL input code and has a comment:

    >>> loc = locale.getlocale()  # get current locale

    And is copied with the comment:

    loc = locale.getlocale()  # get current locale

    But this isn't really a comment, it's REPL output shown as if it was a comment:

    # use German locale; name might vary with platform

    And output isn't copied.

  15. StanFromIreland commented on Apr 18, 2025

    @StanFromIreland
    Member

    I guess I can move the comment to to follow the line after.

  16. hugovk commented on May 23, 2025

    @hugovk
    Member

    Done in #231 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions