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

python + other languages execution is possible! #242

Closed
AnirudhG07 opened this issue May 18, 2024 · 20 comments
Closed

python + other languages execution is possible! #242

AnirudhG07 opened this issue May 18, 2024 · 20 comments

Comments

@AnirudhG07
Copy link

AnirudhG07 commented May 18, 2024

@mfontanini I was wonder if I could actually run python in the bash script for exec.
So here is what i came up with!

```bash +exec
#!/bin/bash

# Run Python code directly
python -c """
for i in range(5):
    print(f'Hello, Mr. {i})
""" // the triple quotes are important
\```   // ignore the backslash

This is very much successful!
The output goes like-

       ——————————————— [done] ———————————

        Hello Mr.0!
        Hello Mr.1!
        Hello Mr.2!
        Hello Mr.3!
        Hello Mr.4!

It is amazing!
Now I would like to contribute based on this idea to the demo.md
And if we could make this as-

```python +exec
# python code goes here
\``` // again ignore the backslash

Then it would be great!

@AnirudhG07 AnirudhG07 changed the title python execution is possible! python + other languages execution is possible! May 18, 2024
@AnirudhG07
Copy link
Author

AnirudhG07 commented May 18, 2024

Not just python, we can execute more languages using that hack that you can run everything through bash.
Here is an overview

```bash +exec
#!/bin/bash

# Run Python code
python -c """
print('Hello from Python!')
"""

# Write and run Java code
cat > HelloWorld.java << EOF
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello from Java!");
    }
}
EOF
javac HelloWorld.java
java HelloWorld

# Write and run JavaScript code
cat > hello.js << EOF
console.log('Hello from JavaScript!');
EOF
node hello.js

# Write and run Rust code
cat > hello.rs << EOF
fn main() {
    println!("Hello from Rust!");
}
EOF
rustc hello.rs
./hello

# Write and run Go code
cat > hello.go << EOF
package main
import "fmt"
func main() {
    fmt.Println("Hello from Go!")
}
EOF
go run hello.go

# Write and run C code
cat > hello.c << EOF
#include <stdio.h>
int main() {
    printf("Hello from C!\n");
    return 0;
}
EOF
gcc hello.c -o hello
./hello

# Write and run C++ code
cat > hello.cpp << EOF
#include <iostream>
int main() {
    std::cout << "Hello from C++!" << std::endl;
    return 0;
}
EOF
g++ hello.cpp -o hello
./hello

# Write and run C# code
cat > hello.cs << EOF
using System;
class Program {
    static void Main() {
        Console.WriteLine("Hello from C#!");
    }
}
EOF
mcs hello.cs
mono hello.exe
\```

and so on...

I have tested and every single one of these work.
Now we can add more functionalities to exec!

@AnirudhG07
Copy link
Author

AnirudhG07 commented May 18, 2024

I ll be putting up a languages.md where every language code can be executed
And hopefully we can use this common-ish system to get make code execution possible easily!

@mfontanini
Copy link
Owner

I think ideally if this is to be supported properly, it should be via a python +exec type of block and not via bash. Mostly because it just looks weird. e.g. if you want to show a python snippet and what it outputs, you'd expect to see properly highlighted python code that executes, not a bash script with a multi-line script containing non highlighted code.

I don't know about adding an example presentation for this, unless it's using the proper implementation ^. As I said above, this looks a bit strange so I don't want to advertise it as "multiple language execution support" unless it looks correct. I didn't bother adding this yet because nobody's asked about it and it opens a pandora's box where we would need to support potentially lots of languages and I don't know if I have the time to figure out how to execute them all and ensure it works correctly for all of them.

@AnirudhG07
Copy link
Author

True.
Well i could suggest a parsing way around that, if python +exec is written or any other language, we can make it assume it is bash +exec + making temp file code, soo whatever language is there, u can make a dummy file and remove it after execution as suggested.
It always has its disadvantages - environment satisfactions or the dummy file name previously present, etc.

@AnirudhG07
Copy link
Author

But for those who want to use and show some execution of other language codes can always try this way out(like i needed python). Though not ideal.

@dmackdev
Copy link
Contributor

dmackdev commented May 20, 2024

Have you considered an approach like patat?

You use the front matter to register "evaluators", which each contain the command you want. Then you can reference the evaluator in the slides alongside the existing code highlighter block. This way, presenterm wouldn't need to explicitly support every language, and you can display/highlight code as before.

Any thoughts @mfontanini?

@dmackdev
Copy link
Contributor

I am experimenting with a couple of approaches - see dmackdev#1 for an example of one of them that uses your code snippet examples @AnirudhG07.

@AnirudhG07
Copy link
Author

AnirudhG07 commented May 25, 2024

@dmackdev you can use the code snippets from PR #243 where i have explained use case for different types of languages in languages.md file.
The mentioned PR looks amazing. Let me know how i can contribute to this idea.

@mfontanini
Copy link
Owner

@dmackdev I like the patat idea of having executors be part of your configuration. However, without looking too deep into how patat does this, I think that works well for interpreted languages but probably not so much for compiled ones. In the latter execution is a two step process so the command definition could be a bit annoying.

What I was thinking of would be to have this in a somewhat similar way but not exactly that way. Instead of having this be a part of a presentation's front matter, we could have shell scripts per language that define how to execute it. We could have a set of builtins and let you extend them locally by dropping a file in a directory like ~/.config/presenterm/executors/<language>.sh. The idea would be that each executor would be a (hopefully very simple) shell script that receives the code snippet's path as a parameter and does everything required to build/run it. The stdout output is what presenterm would show as part of the slide when invoking it. So e.g. for python you could have

#!/usr/bin/env bash

python $1

For other ones like java you'd use javac and java internally, but the idea would be the same.

On presenterm's side we'd need to:

  1. Have a directory in the repo where executors are defined and they'd be embedded into the binary at build time (via build.rs similar to how themes are embedded now).
  2. Upon execution of any non shell script code (as this is the only one really "built in" into the binary), we'd create a temp file, write the contents of the snippet, and run it.
  3. The running part would be the same code that currently runs shell scripts so we'd pull the stdout and display it just like we do for shell scripts without doing any extra work.

I think this makes it:

  1. Easy for people to contribute executors. No need to touch the source code as the scripts will be all in a directory in the repo and would be embedded into presenterm at build time.
  2. Easy for people to extend it locally in case either the built in executor for a language somehow doesn't work for them (meaning you can override them) or because they want to add an executor quickly and don't have the time to contribute upstream.

Thoughts? This also seems to be similar to how slides does it, except they define the executors as part of the code which I'd like to avoid (mainly because I think it's just easier to have shell scripts than having to define commands inside the rust code).


PS: the solution you're suggesting in your branch works but is IMO a bit too involved for users. I'd prefer if the steps to get code running was defined once and everyone could benefit from it. That is, I don't think people would need/want to go through the effort of doing the javac / java / rm dance to get java code to run.

@dmackdev
Copy link
Contributor

dmackdev commented May 25, 2024

I think that works well for interpreted languages but probably not so much for compiled ones.

I had the same observation too - I experimented with an executors/evaluators approach and I resorted to using cargo-script for the command in a Rust code evaluator. See here for reference.

@dmackdev
Copy link
Contributor

dmackdev commented May 25, 2024

PS: the solution you're suggesting in your branch works but is IMO a bit too involved for users.

That may be the case, and it certainly would be a lot of repetition in the markdown for multiple code snippets.

However, I wonder if the "hidden code lines that still execute" feature may still be desirable, but to serve a different use case: namely to be able to display only the main focus of the code snippet, without displaying the setup/boilerplate code. This is one of the motivations of this feature in rustdocs.

This way, the code in the markdown would be complete (albeit with the delimiters), and any other script that executes the code like in your suggested approach can assume the code snippet is a valid program, whilst providing users with the ability to focus/show only the relevant lines of code.

What I mean is, if I only wanted to show the Java line:

System.out.println("Hello from Java!");

the script that executes it should not be required to wrap it in a class and a main method in order to create a valid Java program. But presenters can still hide the enclosing class and main method to show only that line.

I suppose there might be some more work involved to make sure presenterm's features for displaying line numbers, and selective/dynamic line highlighting still work with hidden code lines.

@dmackdev
Copy link
Contributor

Thoughts? This also seems to be similar to how slides does it, except they define the executors as part of the code which I'd like to avoid (mainly because I think it's just easier to have shell scripts than having to define commands inside the rust code).

I like your approach - it certainly would be easier to configure/test standalone scripts compared to trying to express them in YAML in the front matter.

So is your thinking that presenterm would have a set of "vanilla/default" built-in executor scripts provided for each language? And if a presenter wanted, for example, specific compiler flags or options when executing code, they could just create their own executor shell script in one of the presenterm config directories?

What would also be great is if your approach could respect configurations local to the presentation file directory, so I could co-locate the presentation and the code executor script in the same repository. Or I guess you could use the --config-path parameter when running presenterm?

@mfontanini
Copy link
Owner

However, I wonder if the "hidden code lines that still execute" feature may still be desirable, but to serve a different use case: namely to be able to display only the main focus of the code snippet, without displaying the setup/boilerplate code. This is one of the motivations of this feature in rustdocs.

Yes, I agree. Your solution addresses a problem that will become more evident once language execution works. And as you say, it will likely require a bunch more work.

So is your thinking that presenterm would have a set of "vanilla/default" built-in executor scripts provided for each language? And if a presenter wanted, for example, specific compiler flags or options when executing code, they could just create their own executor shell script in one of the presenterm config directories?

Yes, exactly.

What would also be great is if your approach could respect configurations local to the presentation file directory, so I could co-locate the presentation and the code executor script in the same repository. Or I guess you could use the --config-path parameter when running presenterm?

Yeah we can also let you override this per presentation. e.g. the config file (which can be overriden already on a per presentation basis) can have a path to where executors are. So you could override that in a presentation and point to . or something. I'd need to check the specifics but it's doable and fits into how things already work.

@mfontanini
Copy link
Owner

@dmackdev I'll work on this now that you fixed the outstanding issues. Having proper stderr handling is a must now that we'll be invoking programs that may fail on purpose (e.g. "this is how the borrow checker errors when you do this in rust").

@dmackdev
Copy link
Contributor

Sounds good!

BTW I have created a new branch for the hidden code lines that removes any support for executing code in languages other than bash. Appreciate this feature might be a bit premature, but I appear to have it respecting line numbers, selective highlighting and dynamic highlighting - see the example presentation. What do you think?

https://github.com/mfontanini/presenterm/compare/master...dmackdev:presenterm:feat/support-hidden-code-lines?expand=1

@mfontanini
Copy link
Owner

I just created a PR to add the groundwork for this + the python executor as a showcase. Somehow the output is being buffered so doing things like sleep inside the python script doesn't reflect in real time in the presentation. I looked a bit at this but didn't get anywhere concrete; I'll try to look again once I have more time.

@mfontanini
Copy link
Owner

What do you think?

I gotta run now so I'll have a look later / tomorrow

@mfontanini
Copy link
Owner

Alright, I merged the PR. So now we have python code execution and adding more languages implies simply adding a shell script.

@mfontanini
Copy link
Owner

@dmackdev I think it would be nice if we didn't use /// for every language but this was configurable instead (defaulting to nothing so we don't have to preemptively define it for every language we support highlighting for). /// is valid rust so that would mean I can no longer use doc comments in code snippets, and even worse it would be pretty confusing if you don't know about this, add a /// section and then it doesn't show up in the presentation.

I like the doc tests approach in rust better where # is used to hide lines, see this. Could this be made optional and language-defined so CodeLanguage has some fn hidden_line_prefix(&self) -> Option<&'static str> that can be used to configure this? If you'd like to create the PR and move the conversation there that'd be great.

@mfontanini
Copy link
Owner

I just added support for a bunch more languages in #255. Closing this as the bulk of the work is done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants