expressive-code / expressive-code

A text marking & annotation engine for presenting source code on the web.

Home Page:https://expressive-code.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Feature: multiple copy buttons for command blocks

HiDeoo opened this issue · comments

While searching the Bun documentation for a specific API, I landed on their documentation quickstart and noticed that their bash code blocks which contains multiple commands have a copy button for each command.

image

I think this is a great idea considering users usually want to copy a single command at a time and not the whole block of commands and it also avoids having to create a separate code block for each command to get the same result.

I wanted to know if this could be something that could be interesting to add to Expressive Code at some point?

Thanks again for this great project!

Hey @HiDeoo, thank you for this feature idea!

I like the idea of being able to copy terminal commands individually. We already introduced a bit of extra handling for terminal windows a while back by excluding comments from the copied terminal contents by default, and I think this could be a natural continuation of this path.

However, I think it might be a bit tricky to get this feature right and we should discuss this further before jumping into an implementation. Here are two possible discussion points I could think of right now:

  1. Should these single-command copy buttons be an addition or an alternative to the copy buttons for the entire block?

    • My personal preference here would be that this is an either/or scenario: When using the single-command copy buttons in a terminal snippet, the button to copy the entire contents would not be displayed anymore.
  2. Should we differentiate between commands and their output when deciding where to display copy buttons?

    • I've often seen terminal snippets that do not only show the command(s) to run, but also the expected command output. Do we agree that we don't want to show copy buttons on the output lines?
    • If so, how can we allow authors to differentiate between terminal commands and their output when writing terminal snippets? We should always try to find solutions that look completely natural and easily understandable in the raw markdown file.
    • Personal preference: I could imagine that lines with "prompt-like beginnings" (e.g. $ , > , PS > ) get detected in terminal snippets and cause their parent snippet to switch to the "single-command copy buttons" mode. The prompts themselves would be excluded from the copied line as well then. However, I'm not sure if these prompts cover all the typically used ones, if they need to support optional CWD parts, how likely such an auto-detection would trigger false positives, and how authors can escape text that is incorrectly detected as a prompt.

I probably missed further topics that are worth discussing as well. Please let me know your thoughts, everyone reading this! :)

Just another quick note: I could now look at the Bun docs you linked, and it seems that they have also thought of these points and found solutions to them. However, I couldn't see how they are building their docs - does anyone know more? We might want to look into existing solutions to take inspiration from.

I think it might be a bit tricky to get this feature right and we should discuss this further before jumping into an implementation.

Definitely, it's not something that is "missing" in Expressive Code per se, but it's something that I think would be a nice addition at some point and taking the time to make sure it's done right is important.

Should these single-command copy buttons be an addition or an alternative to the copy buttons for the entire block?

I agree with you on that point, I think it would not make a lot of sense to have both at the same time.

Do we agree that we don't want to show copy buttons on the output lines?

I personally 100% agree with that, I think it would be confusing to have copy buttons on the output lines.

I could imagine that lines with "prompt-like beginnings" (e.g. $ , > , PS > ) get detected in terminal snippets and cause their parent snippet to switch to the "single-command copy buttons" mode.

That was my first idea as well and you raise some really good points about the potential issues with that approach.

I'd like to make some research if this is something that already exists in public libraries, how they do it as they might have already solved some of the issues we are discussing here. Maybe some other people may have some ideas as well.

However, I couldn't see how they are building their docs

The docs are using Markdoc, the Markdown content is in the main repo but the website itself (and the custom Markdoc node they are probably using for this) is not public as far as I know.

https://discord.com/channels/830184174198718474/1070481941863878697/1179346743066902632
Quote from Hippo when I offered a hand to help on this and where:

That‘s a really awesome offer, thank you! Hmm, let me think… the topic I could use help with the most is how to detect what lines are prompts in a terminal snippet. If possible, I‘d like to avoid that users have to add any unnatural-looking special syntax to their terminal snippets and that it just magically autodetects prompts, without a high risk for false positives. On top of that, there should be a simple syntax to force the start of a line to be treated as a prompt in case the autodetection doesn’t work for some special cases, and maybe also an escape syntax to prevent a line from being treated as a prompt. In the end, all of this should result in a classifier function I can pass a raw terminal line to and that returns the prompt (if any was found) and the rest of the line (that would end up being copied to the clipboard).

If I had such a function and a couple of tests that show all cases it covers, that would be a lot of help!

We can continue discussing this in the issue I linked. If you don’t feel like diving into the code, commenting on the prompt syntax idea there would also already help a lot!

I'll get my feet wet into the source code before anything. Just leaving this here for reference.

To add a new situation

sudo apt update
sudo apt install libwebkit2gtk-4.0-dev \
    build-essential \
    curl \
    wget \
    file \
    libssl-dev \
    libgtk-3-dev \
    libayatana-appindicator3-dev \
    librsvg2-dev

this would be 2 commands.

I did a quick search around existing documentations (around 10) and not one had this feature, even having a single copy to cplipboard was not that common.

Something related worth noting are the google guidelines for formatting command line docs https://developers.google.com/style/code-syntax#prompt

There's probably other guides with similar advice, but this one is neat.

Particularly:

If your command-line instructions show multiple lines of input in one block, then start each line of input with the prompt symbol. If you don't want users to copy the prompt symbol when they copy the command, you might be able to turn off text selection for the symbol—for example, by using CSS.

Just so we are on the same page, @hippotastic what do you meant by terminal vs prompt:

Something like this?
this is the terminal snippet

pwd
> /Users/documents/project

and the command is pwd


$ brew install somepackage

command is brew install somepackage


and an over the edge case

PS C:\> Get-ChildItem -Path C:\Temp\

Directory:  C:\Temp

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d-----        5/15/2019   6:45 AM        1   One
d-----        5/15/2019   6:45 AM        1   Two
d-----        5/15/2019   6:45 AM        1   Three

command is Get-ChildItem -Path C:\Temp\

anyways... then this function could exist outside expressive-code. It just needs to receive the prompt snippet (line by line or a formatted block?) and then return the command? am I tripping or this is it?

this is interesting, when clicking the line it copies the line, there's the possibility to select multiple lines
https://codedoc.cc/
firefox_89Q6hGlNPp
plus an idea we could use:
image
create a sort of fence to allow the user to separate what is code and output, that would def simplify the magic we would have to do. I haven't look through it's code yet
https://github.com/CONNECT-platform/codedoc/

Oh god, this really seems to be a rather deep rabbit hole!

After looking at your examples, and also testing how the various available terminal highlighting grammars highlight prompts, commands and output (I tested sh, ps, cmd, console - the rest seem to be aliases), I feel like getting terminals right requires multiple problems to be solved:

  1. I agree that we need some way to separate command input from command output. Ideally, the command output should be removed from the text being processed by the terminal highlighting language, because it's completely dependent on the command being run and therefore syntax highlighters have no way to properly highlight it.

    One possible solution that I see for this sub-problem is tagging command output with @output (for single lines), or @output-start ... @output-end pairs for multi-line command output. This is what it would look like:

    ```ps
    PS D:\Dev\demo-code\docs> pnpm dev
    
    # @output-start
    > @internal/docs@0.0.1 dev D:\Dev\demo-code\docs
    > astro dev
    
      astro  v3.5.0 started in 1835ms
    
      ┃ Local    http://localhost:4321/
      ┃ Network  use --host to expose
    
    10:51:52 [content] Watching src/content/ for changes
    10:51:53 [content] Types generated
    10:51:53 [astro] update /.astro/types.d.ts
    # @output-end
    
    PS D:\Dev\demo-code\docs> pnpm build
    ```

    Note that the > prefixes in the first two output lines are included in the actual output from pnpm on my machine and were not added by me. This is why I'd also avoid using > as some special marker for output like Codedoc does. The reason why I'm using @-prefixed tags here is that I'm planning to use this as an Expressive Code-wide syntax pattern for other types of annotations as well.

  2. After the output has been separated from the input like this, we should be able to view a terminal code snippet as a series of "prompt + command" blocks with optional "output" blocks between them. As you said, this should hopefully simplify the remaining task of separating prompts from commands.

    I think we should at least support the following types of prompts without a path:

    $ pnpm ls
    % pnpm ls
    > pnpm ls
    PS > pnpm ls
    

    It would be great if we could also support common prompts with additional data like user and path:

    user@MyMachine:/mnt/d/dev/with spaces/test$ pnpm ls
    user@MyMachine MacOS folder with spaces % pnpm ls
    C:\Users\Default\With Spaces\Test> pnpm ls
    PS C:\Users\Default\With Spaces\Test> pnpm ls
    

    I'd probably not try to support multi-line commands in the first version, as these introduce yet another level of complexity, both in classification and in rendering. I think we might add this later.

  3. The way Codedoc does line selection and copying is really interesting to look at, but it felt a bit confusing to use for me. I'm not a big fan of sites taking away my ability to select text and replacing it with something else that doesn't behave the way I'm used to. So I'd probably stay away from this particular way of copying single and multiple lines in order not to confuse users.

What do you think?

About 1 and 2: Considering you are planning @ prefixed tags, then what about a @copy tag for each line and @copy-start @copy-end for multi line?
Then I guess those tags should be opt-in as a way to make sure every @tag is used purposely and don't conflict with what the user may've written, this I'm not sure which way to go.

@copy $ pnpm i     [copy button]
@copy $ pnpm dev   [copy button]

I think we should at least support the following types of prompts without a path [...]

Also I'd say it should be made clear in the docs that the ES user can add PS > and that wouldn't be copied. On the other hand, I think it would be nice it being added on each line based on the current syntax chosen, along with your upcoming line number feature 🤭

About 3: Yes, agree. I'd go with boring and predictable 100% most of the time


Now I'm just thinking out loud:

would something like

<es syntax="bash">
user@MyMachine:/mnt/d/dev/with spaces/$ `cd test`
user@MyMachine:/mnt/d/dev/with spaces/test$ `ls`
<es/>

render to

user@MyMachine:/mnt/d/dev/with spaces/$ cd test   [copy button]
user@MyMachine:/mnt/d/dev/with spaces/test$ ls    [copy button]

Lastly, in the case where the path is visible, I think about the docs end user seeing this and their expectation is that everything will be copied, not because it's the right way, but because it's common (expected), so they copy manually not knowing they could click the button to get it clear, ofc in multiple situations eventually they will realize, i hope

Idk, a hint to make it clear what is going to be copied or not would be nice, maybe dim the path a bit.

About 1 and 2: Considering you are planning @ prefixed tags, then what about a @copy tag for each line and @copy-start @copy-end for multi line? Then I guess those tags should be opt-in as a way to make sure every @tag is used purposely and don't conflict with what the user may've written, this I'm not sure which way to go.

@copy $ pnpm i     [copy button]
@copy $ pnpm dev   [copy button]

I feel like this would be too much additional tags just to get two copy buttons on a terminal snippet. When you look back at HiDeoo's original example, I'd like to keep the syntax to achieve that result as simple as this:

```sh
$ mkdir quickstart
$ cd quickstart
```

I see this (just showing a few commands to be copied) as the primary (most common) use case which should require no additional syntax for the docs author.

Additional features like fencing off output are more advanced use cases and therefore I would reserve the need to add additional syntax/tags to those cases.

(Also, just as a side note, the planned syntax for the @ tags is only valid in comments, so it would always need to be prefixed by a valid comment start in the language, e.g. # @copy.)


Also I'd say it should be made clear in the docs that the ES user can add PS > and that wouldn't be copied.

Agreed, the supported prompt syntaxes must be added to the EC docs.


Lastly, in the case where the path is visible, I think about the docs end user seeing this and their expectation is that everything will be copied, not because it's the right way, but because it's common (expected), so they copy manually not knowing they could click the button to get it clear, ofc in multiple situations eventually they will realize, i hope

Idk, a hint to make it clear what is going to be copied or not would be nice, maybe dim the path a bit.

I also agree with this. It should be made clear to the user that the prompt isn't going to be a part of the copied text, which I'd try to make clear by styling it appropriately (dimming the prompt by default, and maybe when hovering over the copy button, dimming it even more).