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

Converting .jl to Pluto notebook #132

Open
dpsanders opened this issue May 31, 2020 · 19 comments
Open

Converting .jl to Pluto notebook #132

dpsanders opened this issue May 31, 2020 · 19 comments
Labels
frontend Concerning the HTML editor help welcome If you are experienced in this topic - let us know!

Comments

@dpsanders
Copy link

I tried to open a standard .jl file in Pluto (from the Pluto startup page) and got

Failed to load notebook:

MethodError(union, (), 0x0000000000006c87)

<a href="/">Go back</a>

It would be great to be able to import a normal .jl and have it Plutified. This should e.g. take blocks of code and make them a single cell.

@fonsp
Copy link
Owner

fonsp commented Jun 1, 2020

I like your optimism in using the Open button and seeing what happens!

I guess that this is easy to add in, but hard to do right. With that I mean that there is lots of new UI needed to explain to the user: what they are doing, what the result will be (no two-way conversion), what went wrong (if the import fails) and where they want to save the notebook after importing (overwrite?).

Still, this might be worthwhile to add in, since it makes it easier to get started with Pluto.

@fonsp fonsp changed the title Can't import a .jl file Importing non-pluto .jl files Jun 1, 2020
@dpsanders
Copy link
Author

I like your optimism in using the Open button and seeing what happens!

Ha. Yes it does seem like a good way to get started with Pluto, but I agree that it seems non-trivial to do correctly.

You could add _pluto to the filename for example, e.g. hello.jl -> hello_pluto.jl, since it's easy enough for the user to change. You don't want to just overwrite their file I guess.

@fonsp
Copy link
Owner

fonsp commented Jun 3, 2020

To anyone taking this on: see also #14, maybe this can be combined with Weave.jl and pandoc and Literate.jl and friends

@fonsp fonsp changed the title Importing non-pluto .jl files Converting .jl to Pluto notebook Jul 11, 2020
@fonsp fonsp added frontend Concerning the HTML editor help welcome If you are experienced in this topic - let us know! labels Jul 11, 2020
@j-fu
Copy link
Contributor

j-fu commented Aug 1, 2020

I think a possible way would be through Literate.jl, which has an option to output to Juypter, that means there is already a way to define markdown and Julia cells. By no means I think that a workflow .jl -> pluto should pass through .ipynb, though.

But may be it is even simpler: IMHO it would be sufficient to have syntax to mark cell boundaries, e.g. by # === which is recognized by Pluto on the first load and converted into the hash voodoo separators. Then we would have the notion of an "empty", "untouched" "proto-" notebook or however we call it and a notebook with recognized cells and manifest cell order.
This could even go into a Pluto.plutoify() method outside of the browser which takes in a proto notebook and spits out a real one (and complains about errors/ambiguities), very much like Literate itself works. This then could even be used for having a cli tool for this purpose.

@fonsp
Copy link
Owner

fonsp commented Aug 12, 2020

Support for writing notebook files by hand is not of this project, and making conversion part of Pluto is requires some carefully designed UI (#132 (comment)). But just like Jupyter conversion (https://observablehq.com/@olivier_plas/pluto-jl-jupyter-conversion and https://github.com/vdayanand/Jupyter2Pluto.jl), this can be an external tool, which is very simple to write! Maybe someone could try making it themselves?

If you run Meta.parse(code_string, 1), then it parses as much as it can into a single expression, and it returns the index of the next expression:

julia> code="s = 1\nr=2"
"s = 1\nr=2"

julia> Meta.parse(code, 1)
(:(s = 1), 7)

You can repeat this until you have found the start indices of every expression, and you then turn those into Pluto cells.

@j-fu
Copy link
Contributor

j-fu commented Aug 12, 2020

Hi is there an API to turn something into a pluto cell (md or code) and to write a bunch of cells to a file? I think this is almost all I need...

@fonsp
Copy link
Owner

fonsp commented Aug 12, 2020

Yep:

julia> import Pluto

julia> code = ["hello", "world()"];

julia> n = Pluto.Notebook(Pluto.Cell.(code));

julia> path = "hello.jl";

julia> Pluto.save_notebook(n, path);

This gives:

julia> read(path, String) |> Text
### A Pluto.jl notebook ###
# v0.11.4

using Markdown
using InteractiveUtils

# ╔═╡ 844da824-dcb6-11ea-1b3d-95c52106a0d2
hello

# ╔═╡ 844da856-dcb6-11ea-2061-a59a23dd029f
world()

# ╔═╡ Cell order:
# ╠═844da824-dcb6-11ea-1b3d-95c52106a0d2
# ╠═844da856-dcb6-11ea-2061-a59a23dd029f

@j-fu
Copy link
Contributor

j-fu commented Aug 18, 2020

Ok, this makes it easy to tweak Literate.script(), see my first attempt below.
I am not sure if @fredrikekre wants to have a Pluto dependency in Literate.jl. It could also go into Pluto it a Literate dependency is ok, or else into a separate package.
To put this into a separate package or into pluto creates some dependencies on internals of Literate, but perhaps this is the best bet.

function pluto(inputfile, outputdir=pwd(); config::Dict=Dict(), kwargs...)
    # Create configuration by merging default and userdefined
    config = Literate.create_configuration(inputfile; user_config=config, user_kwargs=kwargs)

    # normalize paths
    inputfile = normpath(inputfile)
    isfile(inputfile) || throw(ArgumentError("cannot find inputfile `$(inputfile)`"))
    inputfile = realpath(abspath(inputfile))
    mkpath(outputdir)
    outputdir = realpath(abspath(outputdir))

    @info "generating plain script file from `$(Base.contractuser(inputfile))`"
    # read content
    content = read(inputfile, String)

    # run custom pre-processing from user
    content = config["preprocess"](content)

    # default replacements
    content = Literate.replace_default(content, :jl; config=config)

    # create the script file
    chunks = Literate.parse(content)
    code=[]
    for chunk in chunks
        cell = IOBuffer()
        if isa(chunk, Literate.CodeChunk)
            for line in chunk.lines
                write(cell, line, '\n')
            end
            write(cell, '\n') # add a newline between each chunk
        elseif isa(chunk, Literate.MDChunk)
            write(cell,"md\"")
            for line in chunk.lines
                write(cell, rstrip(line.first * line.second * '\n'))
            end
            write(cell, "\"\n") # add a newline between each chunk
        end
        push!(code,String(take!(cell)))
    end

    # custom post-processing from user
    # content = config["postprocess"](String(take!(ioscript)))

    # write to file
    isdir(outputdir) || error("not a directory: $(outputdir)")
    outputfile = joinpath(outputdir, config["name"]::String * ".jl")

    if inputfile == outputfile
        throw(ArgumentError("outputfile (`$outputfile`) is identical to inputfile (`$inputfile`)"))
    end

    @info "writing result to `$(Base.contractuser(outputfile))`"
    nb = Pluto.Notebook(Pluto.Cell.(code));
    Pluto.save_notebook(nb, outputfile);

    return outputfile
end

@fonsp
Copy link
Owner

fonsp commented Aug 19, 2020

Great! I will try it out soon!

About the dependencies problem - I made PlutoUtils.jl for scripts like these, having the Literate.jl dependency is fine there. Can you write a PR?

@j-fu
Copy link
Contributor

j-fu commented Aug 19, 2020

Great! I will try it out soon!

About the dependencies problem - I made PlutoUtils.jl for scripts like these, having the Literate.jl dependency is fine there. Can you write a PR?

Yeah I will do this within a day or two...

@fredrikekre
Copy link
Contributor

It should be pretty trivilal to support something like

Literate.notebook(...; flavor = :pluto)

to Literate without having Pluto as a dependency.

@j-fu
Copy link
Contributor

j-fu commented Aug 20, 2020

For writing a Pluto notebook to disk there is a Pluto API call
While it seems to be not too hard to re-create this in Literate.jl without a Pluto dependency, IMHO his would be hard to maintain
during the evolution of Pluto. So I think it is better to follow Fons' suggestion to put this into PlutoUtils.

@fonsp
Copy link
Owner

fonsp commented Aug 21, 2020

We talked about it here: #311 - the file format will always be backwards compatible, or conversion code is included in Pluto

@fredrikekre
Copy link
Contributor

WIP support for using Literate.jl to create Pluto notebooks from .jl files: fredrikekre/Literate.jl#120. Please try it out!

@j-fu
Copy link
Contributor

j-fu commented Aug 21, 2020

... so you have been faster than my PR to PlutoUtils using the Pluto.Notebook constructor... Have no time for testing this immediately. What I would like to see added are some options like fold_markdown_cells and fold_code_cells (which I however could add in a later PR).
But see also my 2ct in #311 ...

@a9lara
Copy link

a9lara commented Dec 10, 2020

I tried to open a standard .jl file in Pluto (from the Pluto startup page) and got

Failed to load notebook:

MethodError(union, (), 0x0000000000006c87)

<a href="/">Go back</a>

It would be great to be able to import a normal .jl and have it Plutified. This should e.g. take blocks of code and make them a single cell.

Or perhaps just add a "begin" and "end", then run it on Pluto, and once it is opened in Pluto, delete begin and end and ask Pluto to divide the code into cells.

@fonsp
Copy link
Owner

fonsp commented Dec 10, 2020

This won't work, but you can paste the contents into a cell

@jdadavid
Copy link

For the record, I wrote a simple utility for converting plain j.l file to notebook, available at https://github.com/jdadavid/Jl2pluto.jl

@jdadavid
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
frontend Concerning the HTML editor help welcome If you are experienced in this topic - let us know!
Projects
None yet
Development

No branches or pull requests

6 participants