-
Notifications
You must be signed in to change notification settings - Fork 52
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
Exclude unselectable text from being copied #138
Conversation
…electable text should be excluded from copy and do so if needed. [_static/copybutton_funcs.js] Added functionality to get unselectable elements and remove their text.
Thanks for submitting your first pull request! You are awesome! 🤗 |
…t. Fixed typo in Development section.
When running |
This should be merged, @choldgraf. Could you give it a look? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hey @bicarlsen - thanks for this PR! I have a few comments and requests in there, mostly around making sure that this addition is documented clearly so that others can understand it and maintain it in the future.
One other quick question - can we imagine any situation where an author would want a non-selectable text to be copied? If not then I'd be fine just making this happen and not exposing another config key
@@ -118,7 +118,11 @@ const addCopyButtonToCodeCells = () => { | |||
|
|||
var copyTargetText = (trigger) => { | |||
var target = document.querySelector(trigger.attributes['data-clipboard-target'].value); | |||
return formatCopyText(target.innerText, {{ "{!r}".format(copybutton_prompt_text) }}, {{ copybutton_prompt_is_regexp | lower }}, {{ copybutton_only_copy_prompt_lines | lower }}, {{ copybutton_remove_prompts | lower }}, {{ copybutton_copy_empty_lines | lower }}, {{ "{!r}".format(copybutton_line_continuation_character) }}, {{ "{!r}".format(copybutton_here_doc_delimiter) }}) | |||
let text = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you provide a comment about what this is doing? This uses some JS syntax that I haven't seen before (with ?
and :
) and we want this theme to be maintainable for non-JS developers so it'd help to have extra explanation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The p ? t : f
syntax is the ternary operator that returns t
if p
is true and f
otherwise.
@@ -2,10 +2,54 @@ function escapeRegExp(string) { | |||
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string | |||
} | |||
|
|||
function getUnselectable(target) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please provide a high-level docstring that describes what this function does
return unselectable; | ||
} | ||
|
||
function getFilteredText(target, exclude) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here - please provide a docstring
docs/index.rst
Outdated
Exclude unselectable text from copied text | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
By default, ``sphinx-copybutton`` will exclude text that is not selectable by the user. Unselectable text is determined with the CSS style |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By default, ``sphinx-copybutton`` will exclude text that is not selectable by the user. Unselectable text is determined with the CSS style | |
By default, ``sphinx-copybutton`` will exclude text that is not selectable by the user. | |
Unselectable text is determined with the CSS style |
By default, ``sphinx-copybutton`` will exclude text that is not selectable by the user. Unselectable text is determined with the CSS style | |
By default, ``sphinx-copybutton`` will exclude text that is not selectable by the user. Unselectable text is determined with the CSS style |
docs/index.rst
Outdated
Exclude unselectable text from copied text | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
By default, ``sphinx-copybutton`` will exclude text that is not selectable by the user. Unselectable text is determined with the CSS style |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you provide an example of what un-selectable text generally is, so that users understand when they'll likely use this?
One more quick thought - another option would be to let users specify one or more classes that should be removed from the text before copying. That way I think we could simplify the copying logic considerably. For example, if the default class to be excluded was // Clone our target so we don't modify the live DOM
clone = target.cloneNode(true)
// Select the classes we want to remove, and then remove each one
clone.querySelectorAll(".linenos").forEach((node) => (node.remove()))
// This should not have any elements we don't want
textToCopy = clone.innerText; I've just merged a PR to add a couple minimal examples to the docs that should demonstrate this behavior not working. We can use this to confirm that this PR does, in fact, fix the problem :-) |
I can not, but figured I would leave it as an option just in case. I'm happy to remove it though. |
I agree and I mentioned this in regard to Issue #9 as I think it is all tied in together. I'm happy to add that functionality, but wanted to get commetns on it first. |
I do like the simplicity of the implementation in #138 (comment) so unless we can see any downsides to that approach, I'd be fine going that direction |
@choldgraf , I agree about your comment regarding #138 (comment). There is a reason I did it recursively, but can't remember the exact reason now. I'll reimplmenet using the |
I remember why I did it recursively. It's because I exclude @choldgraf , what is your opinion on excluding unselectable |
@bicarlsen sorry for the slow response! My general tendency is to go with the simpler possible where we can, unless there's a strong reason to use the more complex approach. Here, it seems like excluding the |
@choldgraf Great, we should be good to go. I haven't written any tests for it yet, but will do so as soon as I have time. |
@choldgraf just wanted to give this a bump, since it's been a while. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just a quick comment about the config, but this looks pretty good to me.
you mentioned that you wanted to write tests, I think that still needs to happen right?
|
||
.. code-block:: python | ||
|
||
copybutton_exclude = '.exclude_me' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about instead of hard-coding .linenos
, we just set this value to be a list that defaults to ['.linenos']
. Then if somebody wants to define their own exclusion list, they can provide it and include .linenos
as well on their own. This seems a bit simpler
docs/index.rst
Outdated
@@ -395,6 +404,7 @@ For example, to make the copy button visible by default (not just when a code ce | |||
html_css_files = ["custom.css"] | |||
|
|||
See the `Sphinx documentation on custom CSS for more information <https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-html_static_path>`_. | |||
>>>>>>> 6ee971ab8f9c3741d380240c180b1ff47f1d2ddc |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you have some git merge resolution cruft in here
…ith funcitons using .innerText. [package.json] Added browser-env module for DOM support in testing. [docs/index] Updated documentation. [copybutton, copybutton_funcs] Fixed minor errors. Updated default exclude text. [__init__] Updated default exclude text.
I tried to implement testing but apparently it is diffuclt to test functions that use I've included the tests in the |
for more information, see https://pre-commit.ci
I just found a reason why "unselectable text" is better than When rendering shell prompts, the Example: text boxes found in this section: https://coderefinery.org/git-intro/basics/#recording-a-snapshot-with-git So, my vote for the "unselectable text" method (not that it counts for much). |
This became an issue in a workshop, so I did more investigation. But, in my live site, even normal un-selection didn't work. It turns out that not selecting
If you search pygments, I find that So my proposal:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trying to address one of the requests from choldgraf.
And proposal of adding .gp
to the default exclude list, but I'm not yet sure if that is a good idea.
Exclude text | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
You may exclude elements matching CSS selectors from being copied by specifying the `copybutton_exclude` option in ``conf.py``. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may exclude elements matching CSS selectors from being copied by specifying the `copybutton_exclude` option in ``conf.py``. | |
You may exclude elements matching CSS selectors from being copied by specifying the `copybutton_exclude` option in ``conf.py``. | |
For example, a viewer usually doesn't want to copy the line numbers, and CSS provides a way to exclude this. This option | |
implements that option for sphinx-copybutton as well. |
@@ -372,6 +372,17 @@ In this case, all elements that match ``your.selector`` will have a copy button | |||
added to them. | |||
|
|||
|
|||
Exclude text |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exclude text | |
Exclude text from being copied |
|
||
copybutton_exclude = '.exclude_me' | ||
|
||
By default `.linenos` is excluded. If you specify the `copybutton_exclude` option it will replace the default. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By default `.linenos` is excluded. If you specify the `copybutton_exclude` option it will replace the default. | |
By default `.linenos, .gp` are excluded. If you specify the `copybutton_exclude` option it will replace the default. | |
`.linenos` is the Sphinx default for line numbers, and `.gp` is Pygments (the highlighter of Sphinx) for "prompt", e.g. `$` in a shell-session. |
I am unsure if we want .gp
by default, but since sphinx_rtd_theme (and probably others should as well) makes it unselectable by default, it seems reasonable that we mirror that behavior.
But, if we add .gp
by default, then themes which don't un-select .gp
will confuse users. If we do add it, users in themes that understand pygments have to change things. Hm, seeming more like like interpreting the CSS has some sort of advantages but I'm not sure if it's worth the extra work yet...
@@ -68,6 +69,7 @@ def setup(app): | |||
app.add_config_value("copybutton_here_doc_delimiter", "", "html") | |||
app.add_config_value("copybutton_image_path", "copy-button.svg", "html") | |||
app.add_config_value("copybutton_selector", "div.highlight pre", "html") | |||
app.add_config_value("copybutton_exclude", ".linenos", "html") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
app.add_config_value("copybutton_exclude", ".linenos", "html") | |
app.add_config_value("copybutton_exclude", ".linenos, .gp", "html") |
Wait, does this work to select multiple? In the end it gets passed to the filterText function.
For reference, I just found this: Apparently Sphinx basic.css_t excludes the following things: Upstream pygments doesn't seem to do any |
- `.linenos` is the line numbers, and `.gp` is the tag from the pygments highlighter for things like the `$` prompt. Upstream sphinx CSS excludes `.gp` from being copied, see the analysis here: executablebooks#138 (comment) - If upstream sphinx excludes `.gp`, we probably should as well.
- Re-add documentation from PR executablebooks#138 - that PR got conflicted with a docs rearrangement, so I add the docs back in one chunk now. I include my own suggestions in that PR.
#178 is a rebase of this that resolves conflicts with the docs and hopefully includes the suggestions from above. |
In the
sphinx_rtd_theme
I was using the:linenos:
option for theliteralinclude
directive to display line numbers for my code blocks. However, when using thecopybutton
extenstion for users to easily copy the code, the line numbers were being included in the copied text, even though they aren't selectable by the user.I added functionality to exclude this unselectabel text from the copied text, along with an option
copybutton_exclude_unselectable
to determine its behavior. This option is by defaulttrue
.