import Base64: base64decode # from https://github.com/JuliaLang/julia/pull/36425 function detectwsl() Sys.islinux() && isfile("/proc/sys/kernel/osrelease") && occursin(r"Microsoft|WSL"i, read("/proc/sys/kernel/osrelease", String)) end """ maybe_convert_path_to_wsl(path) Return the WSL path if the system is using the Windows Subsystem for Linux (WSL) and return `path` otherwise. WSL mounts the windows drive to /mnt/ and provides a utility tool to convert windows paths into WSL paths. This function will try to use this tool to automagically convert paths pasted from windows (with the right click -> copy as path functionality) into paths Pluto can understand. Example: $(raw"C:\Users\pankg\OneDrive\Desktop\pluto\bakery_pnl_ready2.jl") → "/mnt/c/Users/pankg/OneDrive/Desktop/pluto/bakery_pnl_ready2.jl" but "/mnt/c/Users/pankg/OneDrive/Desktop/pluto/bakery_pnl_ready2.jl" stays the same """ function maybe_convert_path_to_wsl(path) try isfile(path) && return path if detectwsl() # wslpath utility prints path to stderr if it fails to convert # (it used to fail for WSL-valid paths) !isnothing(match(r"^/mnt/\w+/", path)) && return path return readchomp(pipeline(`wslpath -u $(path)`; stderr=devnull)) end catch e return path end return path end const adjectives = [ "groundbreaking" "revolutionary" "important" "novel" "fun" "interesting" "fascinating" "exciting" "surprising" "remarkable" "wonderful" "stunning" "mini" "small" "tiny" "cute" "friendly" "wild" ] const nouns = [ "discovery" "experiment" "story" "journal" "notebook" "revelation" "computation" "creation" "analysis" "invention" "blueprint" "report" "science" "magic" "program" "notes" "lecture" "theory" "proof" "conjecture" ] """ Generate a filename like `"Cute discovery"`. Does not end with `.jl`. """ function cutename() titlecase(rand(adjectives)) * " " * rand(nouns) end function new_notebooks_directory() try path = get( ENV, "JULIA_PLUTO_NEW_NOTEBOOKS_DIR", joinpath(first(DEPOT_PATH), "pluto_notebooks") ) if !isdir(path) mkdir(path) end path catch homedir() end end """ Standard Pluto file extensions, including `.jl` and `.pluto.jl`. Pluto can open files with any extension, but the default extensions are used when searching for notebooks, or when trying to create a nice filename for something else, like the backup file. """ const pluto_file_extensions = [ ".pluto.jl", ".Pluto.jl", ".nb.jl", ".jl", ".plutojl", ".pluto", ".nbjl", ".pljl", ".pluto.jl.txt", # MacOS can create these .txt files sometimes ".jl.txt", ] endswith_pluto_file_extension(s) = any(endswith(s, e) for e in pluto_file_extensions) """ Extract the Julia notebook file contents from a Pluto-exported HTML file. """ function embedded_notebookfile(html_contents::AbstractString)::String if !occursin("</html>", html_contents) throw(ArgumentError("Pass the contents of a Pluto-exported HTML file as argument.")) end m = match(r"pluto_notebookfile.*\"data\:.*base64\,(.*)\"", html_contents) if m === nothing throw(ArgumentError("Notebook does not have an embedded notebook file.")) else String(base64decode(m.captures[1])) end end """ Does the path end with a pluto file extension (like `.jl` or `.pluto.jl`) and does the first line say `### A Pluto.jl notebook ###`? """ is_pluto_notebook(path::String) = endswith_pluto_file_extension(path) && readline(path) == "### A Pluto.jl notebook ###" function without_pluto_file_extension(s) for e in pluto_file_extensions if endswith(s, e) return s[1:prevind(s, ncodeunits(s), ncodeunits(e))] end end s end """ Return `base` * `suffix` if the file does not exist yet. If it does, return `base * sep * string(n) * suffix`, where `n` is the smallest natural number such that the file is new. (no 0 is not a natural number you snake) """ function numbered_until_new(base::AbstractString; sep::AbstractString=" ", suffix::AbstractString=".jl", create_file::Bool=true, skip_original::Bool=false) chosen = base * suffix n = 1 while (n == 1 && skip_original) || isfile(chosen) chosen = base * sep * string(n) * suffix n += 1 end if create_file touch(chosen) end chosen end backup_filename(path) = numbered_until_new(without_pluto_file_extension(path); sep=" backup ", suffix=".jl", create_file=false, skip_original=true) "Like `cp` except we create the file manually (to fix permission issues). (It's not plagiarism if you use this function to copy homework.)" function readwrite(from::AbstractString, to::AbstractString) write(to, read(from, String)) end function tryexpanduser(path) try expanduser(path) catch ex path end end const tamepath = abspath ∘ tryexpanduser "Block until reading the file two times in a row gave the same result." function wait_until_file_unchanged(filename::String, timeout::Real, last_contents::String="")::Nothing new_contents = try read(filename, String) catch "" end @info "Waiting for file to stabilize..."# last_contents new_contents if last_contents == new_contents # yayyy return else sleep(timeout) wait_until_file_unchanged(filename, timeout, new_contents) end end