Code Editor

Edit files with a shell and Python beside them, all in this tab

Loading...

About the Code Editor

A file open on one side and the thing that runs it on the other. Write a script in the editor and press Run, or Ctrl+Enter, and it is saved and run at the prompt underneath as sh deploy.sh or python report.py, which you can also just type. Whatever it writes appears in the tree beside it, because there is one filesystem here rather than one per panel.

This is the same session as the Command Line tool, not a copy of it. A file made by typing touch notes.md shows up in the tree a moment later; a file saved in the editor can be read by the next cat. If a command rewrites a file you have open and have not typed into, the editor takes the change in rather than sitting on the older of two answers. If you have typed into it, your typing stays and the change is left on disk, which is the trade every editor makes.

What runs here

A shell. Pipes, redirects, &&, variables, arrays, if, for, while, case, functions, heredocs and getopts. Around sixty commands, each downloaded the first time you type it. A script here is a script, not a macro.

Python. Real CPython built for WebAssembly, so a traceback is a traceback and the standard library is the standard library. python a.py runs a file, python on its own opens the prompt, and both see the files in the tree: a script that writes out.csv leaves it where you can open it.

JavaScript. The engine the browser already has, so the language is whatever version it speaks and await works at the top of a file. js build.js runs one. It sees the same tree through an fs object with read, write, list and the rest, and it runs somewhere with no network and no storage: a script here can read the files it was handed and print, and that is the whole list.

Ruby. Real CRuby built for WebAssembly. ruby deploy.rb runs a file against the same tree, File.read and File.write are the actual methods, and a backtrace is a backtrace.

Lua. The official 5.4 built for WebAssembly, a quarter of a megabyte. lua build.lua runs a file against the same tree, and io.open and dofile are the real functions.

The editor itself

CodeMirror, with completion, bracket matching, search on Ctrl+F and colouring for Python, shell, JavaScript, Ruby, Lua, HTML, CSS, JSON, YAML, XML and Markdown. A page is coloured as the three languages it is, so a <script> block reads as JavaScript and a <style> block as CSS. The language is worked out from the file name, or from a #! line when the name says nothing, so a script called deploy with no suffix is still coloured as one.

Tabs across the top, with a dot on any file that has changes you have not saved. Ctrl+S, or Cmd+S on a Mac, saves the one you are looking at. Undo is per file.

The two buttons above the tree make a file and a folder. Both land in the folder you last clicked in the tree, or beside the file you have open if you have not clicked one, and the line under the box says which folder that is before you press Enter. A folder you just made is the one they land in next, so making somewhere and then putting something in it is two names typed and nothing else. Renaming and deleting are on the right-click menu of any row.

Three panels: the files down the left, the editor and the prompt sharing the rest. The lines between them are handles, so a deep tree can have the width its names want and a long run of output can have the height it wants, and where you leave each one is where it is the next time you come back.

The arrows at the right of the tabs fill the window with the whole thing, prompt included, since a file and the thing running it are the pair worth having room for. The bar across the top of the site stays where it is, so leaving is a click on the same button or the Escape key, and the panels keep the sizes you dragged them to on the way in and on the way back.

A picture opens as a picture. Click a .png, .jpg, .gif, .webp or .avif and it is shown at its size, on a checkerboard so a transparent background reads as one, with its dimensions under it. There is nothing to save and no Save button, since editing a PNG in a text editor is not a thing to offer. An .svg is text, so it opens in the editor where you can change it, and serve is how you look at it.

Run appears for a file this session can run, which is Python, shell, JavaScript, Ruby and Lua, and is the same thing as pressing Ctrl+Enter. It saves first, since running the version before the change you just made is a confusing half hour. It goes grey while the prompt is in the middle of something, because a line arriving there would land inside a half-typed loop rather than after it.

Debug appears beside Run for a Python, Ruby or Lua file. Click in the gutter to set a breakpoint, press Debug, and the program runs at the prompt as python --debug file.py until it reaches the dot, where it stands still: the line lights up, a panel under the editor shows the call stack and the locals of whichever frame you pick, and Continue, Step over, Step into, Step out and Stop do what they say. The dots follow your edits, stepping into an imported file lands in the file it landed in, and a plain Run never pays for any of it. It is the real interpreter pausing, not a simulation: the whole worker stands still on shared memory until a button answers. JavaScript is the one runnable language without it, because the browser's engine offers a worker no line hook to pause on.

Preview appears for an HTML file and opens the page beside the file, on a divider you can drag. One click is the whole gesture: it saves, starts serve ~ 5500 & at the prompt if nothing is answering there yet, and shows what comes back. Nothing else is asked of you, and nothing about it leaves the tab, since the page is served by a command running in this window. Save again and the preview reloads, so a stylesheet you just changed is the one you are looking at.

Where your files live

In this browser, and nowhere else. They are kept between visits, so closing the tab and coming back tomorrow finds the tree where you left it. There is no account, no server and no upload; what is kept is kept on this machine, and only this page can reach it.

The plain version of that: a file you write here is on the disk of the computer you wrote it on until you remove it. rm removes one at the prompt, and Forget everything under the tree removes all of them and the record of them together. On a shared machine, that button is the thing to reach for.

Files only. Variables, functions and Python’s names last as long as the tab, which is the split every real machine makes: what is in a file is on the disk, and what is in a shell dies with the shell.

The same split decides what happens with two tabs open. One disk, so a file written in the Command Line in another window appears in this tree straight away, and a file removed there disappears from it. Two shells, so each tab keeps its own working directory, variables and Python names. Editors on a real machine work the same way, and so does Forget everything: it empties every window, not the one it was pressed in.

Saving in two windows at once does not cost you either save. Each window keeps the files it changed and they are put together, which covers the case that actually happens, two tabs on different files. A file both windows changed in the same moment is the one thing that has to be decided: this window wins it, and a line under the tree says which file it was. What you have open in a tab here is never at risk either way, because a draft you have not saved is not a file yet.

To take work off this machine, save report.py downloads a file the ordinary way, save on its own takes the whole tree as a zip, and load opens a file picker to bring files back in.

What it is not

There is no npm install and no pip install. This site talks to no other host, which is why the Python runtime is served from here rather than from a CDN, and a package manager is a request to somebody else’s server by definition. What you get is the language and its standard library, which is enough for a surprising amount and is not enough for a project with a dependency list.

There is a git now, local only: git init at the prompt underneath, and the history is ordinary files in this tree. There is a server too, though not the kind that is somewhere else: serve starts one in this tab, Preview is that command with a button on it, and a page it answers is a page nobody outside this browser could have asked for.

Frequently Asked Questions

What is the Code Editor?

It is a code editor with a file tree on one side and a working shell underneath it, all running in the browser tab. You write a script and press Run, or Ctrl+Enter, and it is saved and run at the prompt below as "sh deploy.sh" or "python report.py", which you are equally free to type yourself. Whatever the script writes appears in the tree, because the editor, the prompt and Python are all looking at one filesystem rather than at three copies of one. It shares that filesystem with the Command Line tool, so a file made at either place is available at the other.

What languages can I actually run?

Five, and all of them are real, plus SQL. The shell is a shell: pipes, redirects, variables, if, for, while, case, functions, heredocs and getopts, with around sixty commands behind them. Python is CPython compiled to WebAssembly, so it is the actual interpreter with the actual standard library, and a traceback is the traceback you would get anywhere else. JavaScript is your browser's own engine, reached with "js build.js" for a file or "js" on its own for a prompt where names persist, expressions echo and await works. Ruby is CRuby compiled to WebAssembly on the same terms as the Python: "ruby script.rb" runs a file, "ruby" or "irb" opens a prompt that echoes every value the way irb does, and File.read and File.write are the real methods seeing your real tree. Lua is the official Lua 5.4, a quarter of a megabyte for the whole language, with a prompt that writes tables out and an io.open that sees the same files. SQL comes as sqlite3, the official SQLite against a database file in the same tree, with the box-drawing prompt, the dot-commands and a .import for your CSVs. All of them read and write the same tree, so a script that produces out.csv leaves it somewhere you can open and look at. What none of them has is a package manager: no pip, no npm, no gem and no import, because fetching a package means asking somebody else's server for it.

Is the git real git?

Real enough that the .git directory is byte-for-byte a git repository: isomorphic-git writes the same objects git itself writes, so a folder zipped out of the terminal opens on any machine with its history intact, "git log" and all. The local verbs are there and behave like the manual says: init, status, add, commit, log, diff, branch, checkout, stash, reset and config, with .gitignore honoured and "git add ." staging deletions too. What is missing is missing on purpose: no clone, push, pull or fetch, because nothing on this site talks to another host, so there are no remotes to talk to. Commits are signed "you <you@this-tab>" until git config user.name says who you are.

How does the debugger work?

Click in the gutter of a Python, Ruby or Lua file to set a breakpoint, press Debug, and the file runs at the prompt as "python --debug file.py" (or ruby, or lua) until it reaches the dot. There it stands still: the line lights up, and a panel under the editor shows the call stack and the locals of whichever frame you pick, with Continue, Step over, Step into, Step out and Stop as the answers. It is the official interpreter genuinely paused, a trace hook holding the whole run on shared memory until you press something, not a re-implementation walking your code. The dots move with your edits, a breakpoint inside an imported file pauses there too, and a plain Run never arms the hook, so ordinary runs cost nothing extra. One protocol underneath, per-language hooks on top: sys.settrace for Python, TracePoint for Ruby, debug.sethook for Lua. JavaScript sits it out, because the browser engine offers a worker no line hook.

Are my files uploaded anywhere?

No. Everything runs inside the tab: there is no server behind this page, no account, and no request to any other host. Your files are kept, but they are kept in this browser and nowhere else, so a reload or a visit tomorrow finds them where you left them and nobody else ever sees them. The plain version: a file you write here is on the disk of the computer you wrote it on until you remove it, so on a shared machine use the Forget everything button under the file tree, which deletes every file and the record of them together. "rm" removes one at a time. Only files are kept; variables, functions and Python names go when the tab does. To take work off the machine, "save" downloads a file, "save" on its own downloads the whole tree as a zip, and "load" brings files back in.

Is it safe to run a JavaScript file somebody sent me?

Safer than running it anywhere else, and still not a reason to stop reading it. A script runs in a worker built for that one run and killed afterwards, so it never touches the page, the terminal or the next script. Inside it there is no network and no storage: fetch, XMLHttpRequest, WebSocket, indexedDB and caches are all absent, and so is Worker, which is what stops a script starting a fresh one and getting them back. So it can read the files it was handed and print, and that is the list. Two honest caveats. That list is a list, written down in the source, and a browser that grows a new way out grows it there too. And "the files it was handed" is your whole home directory, so a script can still quietly rewrite or delete your work, exactly as a shell script could. Ctrl+C stops one that will not end.

Can I install a package with pip or npm?

No, and the same answer covers require, import, gem install and LuaRocks. This site makes no requests to any other host, which is why the Python, Ruby and Lua runtimes are served from here rather than from a CDN, and a package manager is a request to somebody else's server by definition. So what you have is each language and what it brings with it: Python's standard library, which is json, re, csv, datetime, itertools, collections, math, decimal, hashlib and the rest of it, Ruby's, which is json, csv, set, date and the rest of that one, Lua's, which is string, table, math, io and os, and JavaScript's built-ins, which is everything from JSON and Intl to crypto.subtle. That covers a surprising amount of the scripting people actually do. What it does not cover is a project with a dependency list, and this is the wrong tool for one.

What happens if a command changes a file I have open?

If you have not typed into it, the editor takes the change in and shows you the new text, so a sed or a Python script that rewrote the file is visible immediately rather than being the older of two answers on your screen. If you have typed into it, your typing stays and the change is left on disk, because throwing away unsaved work to make room for a command is the one thing no editor is forgiven for. A file that gets deleted keeps its tab, and saving it puts the file back. The same three answers cover a command run in another tab of this site, since both windows are looking at one filesystem.

Can I have the Code Editor open in two tabs at once?

Yes, and both are looking at the same files rather than at two copies. A file written in one window appears in the other tree straight away, and one deleted disappears from it, with the rules above deciding what happens to anything you have open: a clean tab takes the new text, a tab you have typed into keeps your typing. Each window keeps its own working directory, variables and Python names, because those belong to a shell rather than to a disk. Saving in both at once does not cost you either save: each window keeps the files it changed and the two are put together, which covers two tabs working on different files. A file changed in both windows in the same moment is the one case that has to be decided, and this window wins it, with a line under the tree naming the file so you are not left to find out later. Forget everything empties every window at once.

How do I rename or delete a file?

Right-click it in the tree for Rename and Delete, or use "mv old.py new.py" and "rm old.py" at the prompt, which is the same filesystem either way and so the same result. A rename brings the open tab with it rather than closing it; a delete asks a second time first, and closes whatever tabs were open under it. Folders work the same, and a folder with anything in it says so instead of taking it with it. "mkdir" makes one.

How do I run the file I am editing?

Press Run at the top of the editor, or Ctrl+Enter. It saves the file first and then types the line at the prompt below, so what runs is what is on your screen rather than the version before the change you just made. Run only appears for a file this session can run, which means Python and shell, worked out from the suffix or from a "#!" line when the name says nothing. It goes grey while the prompt is busy, in the middle of a block, or at the Python prompt, because a line arriving in any of those lands somewhere other than where it was meant to. Nothing stops you typing "python report.py" yourself, and the button is only ever typing it for you.