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

directly inserting skipText into js #58

Merged
merged 8 commits into from
Feb 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added doc/_static/test/TEST_COPYBUTTON.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@
#
# html_sidebars = {}

# CopyButton configuration
# copybutton_prompt_text = ">>> "
# copybutton_only_copy_prompt_lines = False
# copybutton_remove_prompts = False
# copybutton_image_path = "test/TEST_COPYBUTTON.png"

# -- Options for HTMLHelp output ---------------------------------------------

Expand Down
101 changes: 67 additions & 34 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Sphinx-copybutton
Sphinx-copybutton does one thing: add little "copy" button to the right
of your code blocks. That's it! It is a lightweight wrapper around the
excellent (and also lightweight) Javascript library
`ClipboardJS <https://clipboardjs.com/>`.
`ClipboardJS <https://clipboardjs.com/>`_.

**Here's an example**

Expand All @@ -27,13 +27,20 @@ And here's a code block, note the copy button to the right!
copy me!

By default, ``sphinx-copybutton`` will remove Python prompts from
each line that begins with them. For example, try copying the text
each line that begins with them. If it finds lines that start with the
prompt text, all *other* lines will not be copied.
For example, try copying the text
below:

.. code-block:: python

>>> a = 2
>>> print(a)
2

>>> b = 'wow'
>>> print(b)
wow

The text that ``sphinx-copybutton`` uses can be configured as well. See
:ref:`configure_copy_text` for more information.
Expand Down Expand Up @@ -70,8 +77,8 @@ extensions list. E.g.:
...
]

When you build your site, your code blocks should now have little copy buttons to their
right. Clicking the button will copy the code inside!
When you build your site, your code blocks should now have little copy buttons
to their right. Clicking the button will copy the code inside!

Customization
=============
Expand All @@ -92,52 +99,78 @@ overwrite sphinx-copybutton's behavior.

.. _configure_copy_text:

Customize the text that is removed during copying
-------------------------------------------------
Strip and configure input prompts for code cells
------------------------------------------------

By default, ``sphinx-copybutton`` will remove Python prompts (">>> ") from
the beginning of each line. To change the text that is removed (or to remove
no text at all), add the following configuration to your ``conf.py`` file:
By default, ``sphinx-copybutton`` will copy the entire contents of a code
block when the button is clicked. For many languages, it is common to
include **input prompts** with your examples, along with the outputs from
running the code.

.. code:: python
``sphinx-copybutton`` provides functionality to both
strip input prompts, as well as *only* select lines that begin with a prompt.
This allows users to click the button and *only* copy the input text,
excluding the prompts and outputs.

copybutton_skip_text = "sometexttoskip"
To define the prompt text that you'd like removed from copied text in your code
blocks, use the following configuration value in your ``conf.py`` file:

Note that this text will only be removed from lines that *begin* with the text.
.. code-block::

Use a different copy button image
---------------------------------
copybutton_prompt_text = "myinputprompt"

When this variable is set, ``sphinx-copybutton`` will remove the prompt from
the beginning of any lines that start with the text you specify. In
addition, *only* the lines that contain prompts will be copied if any are
discovered. If no lines with prompts are found, then the full contents of
the cell will be copied.

For example, to exclude traditional Python prompts from your copied code,
use the following configuration:

.. code-block::

copybutton_prompt_text = ">>> "

To use a different image for your copy buttons, the easiest thing to do is
to add a small bit of javascript to your Sphinx build that points the image
to something new. Follow these steps:
Configure whether *only* lines with prompts are copied
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1. Create a new javascript file in your site's static folder (e.g., `_static/js/custom.js`).
In it, put the following code:
By default, if sphinx-copybutton detects lines that begin with code prompts,
it will *only* copy the text in those lines (after stripping the prompts).
This assumes that the rest of the code block contains outputs that shouldn't
be copied.

.. code-block:: javascript
To disable this behavior, use the following configuration in ``conf.py``:

const updateCopyButtonImages = () => {
const copybuttonimages = document.querySelectorAll('a.copybtn img')
copybuttonimages.forEach((img, index) => {
img.setAttribute('src', 'path-to-new-image.svg')
})
}
.. code-block:: python

copybutton_only_copy_prompt_lines = False

runWhenDOMLoaded(updateCopyButtonImages)
In this case, all lines of the code blocks will be copied after the prompts
are stripped.

Configure whether the input prompts should be stripped
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

2. Add this javascript file to your `conf.py` configuration like so:
By default, sphinx-copybutton will remove the prompt text from lines
according to the value of ``copybutton_prompt_text``.

.. code-block:: python
To disable this behavior and copy the full text of lines with prompts
(for example, if you'd like to copy *only* the lines with prompts, but not
strip the prompts), use the following configuration in ``conf.py``:

.. code-block:: python

def setup(app):
app.add_javascript('js/custom.js');
copybutton_remove_prompts = False

Use a different copy button image
---------------------------------

This will replace the copybutton images each time the page loads!
To use a different image for your copy buttons, do the following:

**If you know of a better way to do this with sphinx, please don't hesitate to
recommend something!**
1. Place the image in the ``_static/`` folder of your site.
2. Set the ``copybutton_image_path`` variable in your ``conf.py`` to be the
path to your image file, **relative to** ``_static/``.

Development
===========
Expand Down
5 changes: 4 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,8 @@
'_static/copybutton.js',
'_static/copy-button.svg',
'_static/clipboard.min.js']},
classifiers=["License :: OSI Approved :: MIT License"]
classifiers=["License :: OSI Approved :: MIT License"],
install_requires=[
"sphinx>=1.8"
]
)
18 changes: 13 additions & 5 deletions sphinx_copybutton/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,26 @@ def scb_static_path(app):
static_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '_static'))
app.config.html_static_path.append(static_path)

def add_skip_text_js(app):
skip_text = app.config['copybutton_skip_text']
app.add_js_file(None, body="var copybuttonSkipText = '{}';".format(skip_text))
def add_to_context(app, config):
# Update the global context
config.html_context.update({'copybutton_prompt_text': config.copybutton_prompt_text})
config.html_context.update({'copybutton_only_copy_prompt_lines': config.copybutton_only_copy_prompt_lines})
config.html_context.update({'copybutton_remove_prompts': config.copybutton_remove_prompts})
config.html_context.update({'copybutton_image_path': config.copybutton_image_path})

def setup(app):
print('Adding copy buttons to code blocks...')
# Add our static path
app.connect('builder-inited', scb_static_path)
app.connect('builder-inited', add_skip_text_js)

# configuration for this tool
app.add_config_value("copybutton_skip_text", ">>> ", "html")
app.add_config_value("copybutton_prompt_text", "", "html")
app.add_config_value("copybutton_only_copy_prompt_lines", True, "html")
app.add_config_value("copybutton_remove_prompts", True, "html")
app.add_config_value("copybutton_image_path", "copy-button.svg", "html")

# Add configuration value to the template
app.connect("config-inited", add_to_context)

# Add relevant code to headers
app.add_css_file('copybutton.css')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,24 +65,39 @@ const temporarilyChangeTooltip = (el, newText) => {
var copyTargetText = (trigger) => {
var target = document.querySelector(trigger.attributes['data-clipboard-target'].value);
var textContent = target.textContent.split('\n');
// Prevent breaking of the copy functionality, for themes which don't
// set copybuttonSkipText properly.
if(! ("copybuttonSkipText" in window)){
var copybuttonSkipText = ">>> ";
console.warn(`sphinx_copybutton:
The theme that was used to generate this document, does not support the setting for 'copybutton_skip_text',
which is why its default of '>>> ' was used.
Please tell the theme developers to include javascript files with the template tag 'js_tag' for sphinx>=1.8.
Example: https://github.com/readthedocs/sphinx_rtd_theme/blob/ab7d388448258a24f8f4fa96dccb69d24f571736/sphinx_rtd_theme/layout.html#L30
`);
}
var copybuttonPromptText = '{{ copybutton_prompt_text }}'; // Inserted from config
var onlyCopyPromptLines = {{ copybutton_only_copy_prompt_lines | lower }}; // Inserted from config
var removePrompts = {{ copybutton_remove_prompts | lower }}; // Inserted from config

textContent.forEach((line, index) => {
if (line.startsWith(copybuttonSkipText)) {
textContent[index] = line.slice(copybuttonSkipText.length)
// Text content line filtering based on prompts (if a prompt text is given)
if (copybuttonPromptText.length > 0) {
// If only copying prompt lines, remove all lines that don't start w/ prompt
if (onlyCopyPromptLines) {
linesWithPrompt = textContent.filter((line) => {
return line.startsWith(copybuttonPromptText) || (line.length == 0); // Keep newlines
});
// Check to make sure we have at least one non-empty line
var nonEmptyLines = linesWithPrompt.filter((line) => {return line.length > 0});
// If we detected lines w/ prompt, then overwrite textContent w/ those lines
if ((linesWithPrompt.length > 0) && (nonEmptyLines.length > 0)) {
textContent = linesWithPrompt;
}
}
// Remove the starting prompt from any remaining lines
if (removePrompts) {
textContent.forEach((line, index) => {
if (line.startsWith(copybuttonPromptText)) {
textContent[index] = line.slice(copybuttonPromptText.length);
}
});
}
});
return textContent.join('\n')
}
textContent = textContent.join('\n');
// Remove a trailing newline to avoid auto-running when pasting
if (textContent.endsWith("\n")) {
textContent = textContent.slice(0, -1)
}
return textContent
}

const addCopyButtonToCodeCells = () => {
Expand All @@ -102,7 +117,7 @@ const addCopyButtonToCodeCells = () => {

const clipboardButton = id =>
`<a class="copybtn o-tooltip--left" style="background-color: ${pre_bg}" data-tooltip="${messages[locale]['copy']}" data-clipboard-target="#${id}">
<img src="${DOCUMENTATION_OPTIONS.URL_ROOT}_static/copy-button.svg" alt="${messages[locale]['copy_to_clipboard']}">
<img src="${DOCUMENTATION_OPTIONS.URL_ROOT}_static/{{ copybutton_image_path }}" alt="${messages[locale]['copy_to_clipboard']}">
</a>`
codeCell.insertAdjacentHTML('afterend', clipboardButton(id))
})
Expand Down