Coder Social home page Coder Social logo

mhuebert / maria Goto Github PK

View Code? Open in Web Editor NEW
467.0 26.0 34.0 12.96 MB

A ClojureScript coding environment for beginners.

Home Page: https://maria.cloud

Clojure 60.27% CSS 1.97% JavaScript 37.73% Shell 0.02%
clojurescript repl cljs-live magic-tree learning beginner

maria's Introduction

Maria

The ClojureScript coding environment for beginners.

(Part editor, part curriculum. A collaborative work by @daveliepmann, @jackrusher, and @mhuebert.)

Why?

Our work is not to teach, but to help the absorbent mind in its work of development. –Maria Montessori

A recurring problem newcomers have with Clojure is that one must learn everything at once: the JVM, and stack traces, and a new editor, and functional programming, and and and.... What if we shielded folks new to Clojure from the complexity of tooling, stack traces, and programming language esoterica? What if we got out of their way and let them feel the power of programming for themselves? This project aims to delay or remove as many of these obstacles as possible so that people can explore.

The Maria editor is a code playground. There is no installation, zero configuration, and explaining how to make it work takes approximately one sentence. Clojure's sharp edges are rounded off, for instance, by wrapping stack traces so errors are presented humanely. A handful of helper functions do the same for types and SVG shapes. We use in-place evaluation because we find it the most effective and humane way to interact with a computer.

The curriculum we use alongside the editor uses progressive disclosure of the language, to prevent overwhelming folks. Instead of explaining all Clojure's many features as a functional, hosted lisp--which is a big topic--we introduce folks to programming and let language features follow. In this spirit, we named the project after pedagogical pioneer Maria Montessori, whose method "stresses the development of a [learner's] own initiative and natural abilities, especially through practical play" and "at their own pace".

Play

So...please go to maria.cloud and play around with it live! :)

Contributing

We welcome your effort to make the beginner's path to Clojure smoother. Here's how:

Contributing to fixing bugs

The most helpful thing you can do is simply play around! 😸 and report bugs as GitHub issues.

Contributing to better error messages

While you play around, please send us any un-wrapped Clojure error you find. We track these in the Error Handling wiki. This is so helpful for making errors easier for new people!

Please edit the wiki directly to add your experience. Or, file a GitHub issue explaining what you were doing and what error came up. Our goal with this is to protect users from jargon-filled error messages, and that requires lots of eyes pointing out all the ways a particular error could cause trouble.

Contributing to the curriculum

Like Clojure itself, the Maria curriculum is open-source, but is not primarily a community effort. We've chosen a particular approach and we write with a particular voice for a particular audience. Therefore other approaches, although valid, may not belong here. With that caveat, those of us who write the curriculum are open to contributions, which have been accepted according to these rough guidelines:

  • for typos, bugs, and obvious mistakes, we actively welcome your issues and pull requests

  • if you built something cool, please show us! :) it may end up in our Gallery of examples, if you're OK with that

  • for more substantial contributions, from suggestions for phrasing up to entirely new modules, we ask that you do the following to be on the same page as us:

    With that shared understanding attained, please get in touch with us by email so we can discuss ideas in depth.

Development

First, copy to your local machine and navigate to the editor directory:

git clone https://github.com/mhuebert/maria.git;
cd maria/editor

Now, within the editor directory, install javascript dependencies and compile stylesheets:

yarn;

build in development mode, start a local server, and live-reload changes:

yarn watch;

When these builds have completed, open your web browser to https://localhost:8701 (note that dev servers are run via https)

running a REPL

The default nrepl port, configured in shadow-cljs.edn, is 7888.

Once connected, the following will print out a list of things you can do:

(require '[shadow.cljs.devtools.api :as shadow])
(shadow/help)

to directly enter the live environment (provided you've opened up your browser):

(shadow.cljs.devtools.api/repl :live)

make a production build:

yarn release;

License

The curriculum is licensed as Creative Commons Attribution 4.0 (CC BY 4.0).

The Shapes and Friendly libraries are distributed under the Eclipse Public License 2.0.

See LICENSE files in relevant subdirectories.

maria's People

Contributors

avidrucker avatar daiyi avatar daveliepmann avatar dependabot[bot] avatar fnumatic avatar jackrusher avatar kekacoutinho avatar mhuebert avatar plexus avatar ryanzebian avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

maria's Issues

Highlight unbalanced parens

When parens are unbalanced, we should highlight the error, which must be fixed to re-enable editor commands. The error position is already returned by magic-tree, we just have to do something with it.

Wonky sexp detection

Seems the code to detect the start/end of the current sexp is still a bit wonky. See the video:

https://youtu.be/NIHWV3FH808

Also seeing a lot of these in the browser console

Error: Unmatched delimiter: %s("]") [at line 191, column 1]

Editor behavior (keymap, selection)

I'm documenting my preferred editor key/mouse behavior in the wiki. Some of these key bindings are time tested Lisp classics, others are things I'm hoping we can do better than anyone else has. :)

js dependencies

Currently the easiest way to fetch a JS dependency is using goog.net.jsloader, recently added to the main build:

(require '[goog.net.jsloader :as jsl])
(jsl/load "https://wzrd.in/standalone/graphql@latest")

It returns an instance of goog.async.Deferred, which is nicely displayed with its loading, completed or error status.

This should be wrapped in a friendly and simple loading function:

(load-js "https://wzrd.in/standalone/graphql@latest")

Which could then enable a nice npm loader, relying on wzrd.in for browserify'd packages:

(load-npm "graphql@latest")

(these functions will be fine for ordinary REPL use. still lacking though for cases where you want to load a whole file, eg. with load-gist, and the body of the file requires dependencies to be loaded before evaluation.)

(doc "puppy") unhelpful

Evaluating doc on something that doesn't have documentation could be nicer. Perhaps add a specific case where the thing isn't a function so we can tell the user why it doesn't have docs?

URL structure for intro and modules

Before we release on the web, we should have a URL structure in place that auto-loads our curriculum. We'd then have a separate path for a scratchpad that starts empty and stores user input in localstorage.

Perhaps

  • / --> docs/quick_intro.cljs
  • /modules/<n> --> docs/module_.cljs (where n is a name or number?)
  • ???

Functions should pretty print when their value is taken

If I put what-is alone on a line and eval it, I get a big blob of Javascript in the right hand pane. We should intercept this sort of evaluation and insert the source for the function if it's available or otherwise some text saying that it's a fn for which we can't show the source.

Show feedback/hint for EOF error

Imbalanced parens are now highlighted with the exception of the EOF condition. (This is because we can't use the same highlight method for a position which is beyond the end of the document.) We should find a way to indicate an EOF error, possibly hinting at a likely source of the error (the start-position of the last, unclosed top-level form)

Command search/popup (like M-X)

Most editors have some way of searching and executing commands by name. This should:

  • tie in to the same commands system as which-key
  • pop up via a key command
  • remember recently executed commands

better titles

I added a <title>, "coding with maria". It's better than nothing but I don't particularly like it. Is "Maria" enough?

"clear output" command

It would be nice to have a button that clears the output panel, so users can have a clean slate but work from the same code.

what-is for shapes

We should make what-is shape-aware. (Should be easy enough to do while converting shapes to protocol-based type rather than special maps).

(what-is (circle 10))
;;=> "a map: a collection of key/value pairs, where each key 'maps' to its corresponding value"

Better handling of invalid code

Maria shows error messages only when the user evaluates a block of code. But behind the scenes, we update an AST of the user's code on every keypress, which enables editor functionality like bracket highlighting and 'eval region'. Unfortunately, if the user's code cannot be parsed into a valid Clojure form, editor functionality breaks because we no longer have a valid AST.

The code we use to parse Clojure is in the magic-tree library.

Objectives

  1. Show syntax errors in the editor at the appropriate time. Some errors, like mismatched brackets, should be displayed immediately, but we should not show error messages for invalid states which are a normal part of inputting code. (eg: the form under the cursor can be ignored, because the user is still typing it; maps with odd numbers of elements can be ignored if the cursor is still inside the map, etc.)

  2. Maintain editor functionality even when code is 'broken'. Eg/ when a map contains an odd number of elements, we still want to be able to traverse the code for bracket highlighting and eval-region.

(source: #19)

Relevant issues:
braintripping/magic-tree#4, communicate errors
braintripping/magic-tree#5, accommodate unbalanced parens
braintripping/magic-tree#6, accommodate odd-element maps
braintripping/magic-tree#7, track cursor positions

Markdown mode

A quasi literate programming mode where text is treated as markdown by default, and fenced code blocks are treated as Clojure.

This mode is triggered by file extensions, with one level of chaining. In some_file.md, we treat fences marked as ```clj as Clojure. In some_file.clj.md (or .cljc.md or .cljs.md) , we treat code fences as Clojure by default.

Maybe use Prosemirror for a seamless rich text experience, still serialized to markdown: https://re-view.io/components/ui-rich-text-markdown

Externs inference bug affects list/vector expressions

This:

(reduce (fn [a x] (conj a (vector (apply + x))))
      []
      [[1 2 3] [4 5 6] [7 8 9]])

... fails in Maria with a message like:

{:type :infer-warning, :extra {:warn-type :target, :form (. cljs.core/PersistentVector -EMPTY-NODE)}, :source (reduce (fn [a x] (conj a (vector (apply + x))))
      []
      [[1 2 3] [4 5 6] [7 8 9]]), :form (reduce (fn [a x] (conj a (vector (apply + x)))) [] [[1 2 3] [4 5 6] [7 8 9]])}

... but works fine in the clojure REPL. It also works in Maria if I change vector to a literal in the above expression, like [(apply + x)].

Lookup source code for precompiled cljs

The source repl-special can only 'see' code that is compiled locally. It would be nice to look up the source for precompiled code remotely, via GitHub or Clojars. This will mean keeping some kind of association between namespaces, artifacts, and repositories.

Autocomplete symbols

User should be shown an autocomplete menu with in-scope symbols populated from the current compiler state. (It would be nice to also autocomplete keywords.)

Highlight or color-code error message output

When an evaluation produces an error, it might be nice for the output to be color-coded, highlighted, or marked in some other way that alerts the user that the output of evaluation is abnormal.

Inspiration from Intro to Racket with Pictures:

Note that DrRacket highlights in pink the expression that triggered the error (but pink highlighting is not shown in this documentation).

Temp storage of evaluated source snippets

Goal: be able to look up the original source of def'd vars (eg. to address #30)

Problem: The compiler state stores the file, line, and column for each var under its :meta key, but :file is always "user_cljs", because that's what we pass to compile-str/eval-str as the 'name' option for every evaluation, and our repl contents are always changing, so we have no way of tracing a def back to is source.

Possible solution: use a unique name for every evaluation, eg. (gensym user_cljs_), and store the evaluated source code in a cache somewhere (maybe do this in cljs-live). Then we can always look up source via the :file key in a var's metadata.

One warning: there may be some trickery around lines and columns. Should check if we are compiling/evaling source code starting with a non-zero line+column offset. If so, we may need to store a starting line+column offset alongside the source.

Namespace browser

Problem: a user has no way to see all of the names that are available in the current namespace.

The dir command lists all of the defs of a namespace, but we should probably have a more helpful namespace browser which also includes referred names, eg. everything in ns-map.

CodeMirror widgets for comment blocks

Any number of contiguous lines which begin with ; should be rendered as a formatted markdown block.

Distinct from #26, which treats an entire file as markdown and handles fenced blocks as code.

I think the best UI for this would be an embedded prosemirror editor, otherwise we have to mess with 'modes' to jump between raw vs. rendered markdown.

  • find comment block regions as the editor changes
  • replace comment blocks with custom element
  • ensure predictable/nice cursor navigation around custom elements
  • render textarea in custom element. figure out how and when to update the underlying text & keep the custom element in sync, including when adding new lines to the comment block.
  • render ProseMirror editor in custom element, should be straightforward if the above issues are fixed.

To keep high performance, we may look at:

  • only render things within the current viewport.
  • only activate a ProseMirror editor when the user clicks to edit, otherwise render the markdown directly

Prevent apocalypse on evaluation of infinite sequence

I'd like to use repeat for some of our examples, and it would be nice if we had an answer for what to show if the user evaluates an infinite sequence.

e.g. I'd like to encourage learners to evalute (repeat (rectangle 20 20)) as part of understanding:

(map colorize
     ["red" "orange" "yellow" "green" "blue" "purple"]
     (repeat (rectangle 20 20)))

Syntax highlighting improvements

  • Some functions, like *, aren't getting highlighted as functions by CodeMirror. (We have a report of confusion from the field because of this).
  • Can we decorate with a class the depth of each expression modulo 2? I'd like to add subtle "zebra highlighting" by setting the background of each expression to an alternating set of very light grays -- like this, but prettier.

`stack` and `line-up` broken

Executing in the app this sample code from maria.views.repl-shapes:

(stack (colorize "red" (rectangle 20 20))
         (colorize "blue" (rectangle 20 20)))

...causes this error:

maria.js:4491 Error: Minified React error #65; visit http://facebook.github.io/react/docs/error-decoder.html?invariant=65&args[]…ight%2020%2C%20%3Astroke%20%22none%22%2C%20%3Afill%20%22red%22%7D%20nil%5D for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
    at r (maria.js:39)
    at d (maria.js:37)
    at new h (maria.js:37)
    at Object.r [as createInternalComponent] (maria.js:38)
    at i (maria.js:39)
    at r (maria.js:37)
    at o (maria.js:39)
    at i (maria.js:39)
    at Object.instantiateChildren (maria.js:37)
    at h._reconcilerInstantiateChildren (maria.js:38)
re_view.render_loop.force_update_BANG_	@	maria.js:4491
re_view.render_loop.flush_BANG_	@	maria.js:4494
re_view.render_loop.render_loop	@	maria.js:4495

From then on, the app is broken and page must be reloaded.

which-key improvements

  • it would be nice if M-enter/M-S-enter were documented there
  • do we have an easy way to import the default CodeMirror bindings into the popup so they’re also documented? (for example, M-up jumps to top of buffer)
  • M- reports a binding for K (kill) but it does nothing, I suppose because C-k is bound to kill and the bindings have been somehow unified?
  • Safari M-1/M-2 doesn't work, and I don't think we can fix it. Probably needs a new binding. :(
  • Copy/Cut are "X selection" rather than "X at point"
  • The "comment line" function behaves bizarrely in both Safari and Chrome (just uncomments the first line of the buffer, no matter where I am)
  • would like to see uneval bound to M-;, uneval-toplevel-form (or comment-toplevel-form) bound to M-A-;
  • Slurp is great, would love some barf to match! (Adding preferred bindings to wiki)

handle editor commands using maria.commands tooling

Currently the info from defcommand is only populating which-key hints, and not actually driving behavior.

Instead of digging in to the CodeMirror keymap system, I think we should capture and handle events ourselves, and clear most (if not all) of the CodeMirror keymap. That way we can specify and document behavior within Maria to the maximum degree.

Commands presently receive a CodeMirror instance (augmented with extra parsing behavior by magic-tree) as the only argument. This may be fine, will have to see what else would make sense to pass to commands as arguments.

Link from `doc` panel to external documentation site

It would be nice to link from the documentation panel produced by doc to the relevant page on https://clojuredocs.org.

Some functions (e.g. circle...which is what new users will be immediately looking at) won't have documentation there, which might make the idea a non-starter. Or, perhaps as a fallback we could link to the Maria source that defines the function?

Availability of REPL/helper functions across namespaces

The problem

Until now we have been using maria.user as a namespace to hold functions that we want to make accessible to users. But what happens if a user creates a new namespace? Then our helper functions disappear.

Solutions?

REPL Specials

One way to make a function available across all namespaces is to use cljs-live.eval/defspecial to define a 'REPL special' function. But REPL-specials are handled differently from ordinary functions because (a) they receive the compiler state and environment as its first two arguments, (b) they are expected to return a map containing a :value or :error key, and (c) they are not evaluated by the self-hosted compiler. So they are not an ideal way to refer more ordinary functions into user-space.

def in cljs.core

In Clojure-land, lucidity/inject (previously 'vinyasa') is a utility for referring names into clojure.core. It uses intern, which is not implemented in ClojureScript, but is in planck, using def under the hood. The approach seems reasonable. I have written a simple version of inject and will push it soon.

One downside to defs in cljs.core is that the doc will indicate that the function lives in cljs.core, which is misleading. We can get around that by copying the original var's metadata into the compiler state; then, (doc my-referred-var) will show original-namespace/my-referred-var.

Relatedly, we may want to look at modifying dir to distinguish between names that have been injected into a namespace, vs. names that were originally defined there.

Language Levels

cc: @jackrusher

This ties into the 'language levels' idea - that we can morph namespaces to accommodate users of different skill levels. What should a command like (language-level :beginner-1) do? Probably some combination of (1) refer helper names into scope, and (2) stub/wrap existing functions to limit the names available to a user in a friendly way (eg. display a message if a user tries to use a real clojure function that isn't available yet for their level). In order to not affect lower-level code, we should probably not modify cljs.core functions directly for these purposes, and instead limit the effects of language-level to the current namespace.

Gist content overwrites editor state from local storage

Maria saves local editor state by url, so that if you refresh the browser your previous changes remain on the screen.

Urls like /gist/some-id cause content from a gist to be asynchronously loaded. Currently, content from the gist will always overwrite whatever you had in local storage.

Add "loading" screen

Currently maria.cloud takes a few seconds to load, and it's unclear when the interface is ready for me to mouse around or type. It would be nice to have a loading splash screen to prevent the user from getting frustrated by trying to do stuff before it's ready.

Save to gist

If the user is logged in to Github it would be great if they could save the current "buffer" to a gist. If the code was loaded from a gist, I would expect to save back to that gist (like "save" functionality in a local editor); otherwise, it would just generate a new gist with the contents of the current editor.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.