| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| // CodeMirror, copyright (c) by Marijn Haverbeke and others | ||
| // Distributed under an MIT license: http://codemirror.net/LICENSE | ||
|
|
||
| (function (mod) { | ||
| if (typeof exports == "object" && typeof module == "object") // CommonJS | ||
| mod(require("../../lib/codemirror"), require("../haskell/haskell")) | ||
| else if (typeof define == "function" && define.amd) // AMD | ||
| define(["../../lib/codemirror", "../haskell/haskell"], mod) | ||
| else // Plain browser env | ||
| mod(CodeMirror) | ||
| })(function (CodeMirror) { | ||
| "use strict" | ||
|
|
||
| CodeMirror.defineMode("haskell-literate", function (config, parserConfig) { | ||
| var baseMode = CodeMirror.getMode(config, (parserConfig && parserConfig.base) || "haskell") | ||
|
|
||
| return { | ||
| startState: function () { | ||
| return { | ||
| inCode: false, | ||
| baseState: CodeMirror.startState(baseMode) | ||
| } | ||
| }, | ||
| token: function (stream, state) { | ||
| if (stream.sol()) { | ||
| if (state.inCode = stream.eat(">")) | ||
| return "meta" | ||
| } | ||
| if (state.inCode) { | ||
| return baseMode.token(stream, state.baseState) | ||
| } else { | ||
| stream.skipToEnd() | ||
| return "comment" | ||
| } | ||
| }, | ||
| innerMode: function (state) { | ||
| return state.inCode ? {state: state.baseState, mode: baseMode} : null | ||
| } | ||
| } | ||
| }, "haskell") | ||
|
|
||
| CodeMirror.defineMIME("text/x-literate-haskell", "haskell-literate") | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,282 @@ | ||
| <!doctype html> | ||
|
|
||
| <title>CodeMirror: Haskell-literate mode</title> | ||
| <meta charset="utf-8"/> | ||
| <link rel=stylesheet href="../../doc/docs.css"> | ||
|
|
||
| <link rel="stylesheet" href="../../lib/codemirror.css"> | ||
| <script src="../../lib/codemirror.js"></script> | ||
| <script src="haskell-literate.js"></script> | ||
| <script src="../haskell/haskell.js"></script> | ||
| <style>.CodeMirror { | ||
| border-top : 1px solid #DDDDDD; | ||
| border-bottom : 1px solid #DDDDDD; | ||
| }</style> | ||
| <div id=nav> | ||
| <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo | ||
| src="../../doc/logo.png"></a> | ||
|
|
||
| <ul> | ||
| <li><a href="../../index.html">Home</a> | ||
| <li><a href="../../doc/manual.html">Manual</a> | ||
| <li><a href="https://github.com/codemirror/codemirror">Code</a> | ||
| </ul> | ||
| <ul> | ||
| <li><a href="../index.html">Language modes</a> | ||
| <li><a class=active href="#">Haskell-literate</a> | ||
| </ul> | ||
| </div> | ||
|
|
||
| <article> | ||
| <h2>Haskell literate mode</h2> | ||
| <form> | ||
| <textarea id="code" name="code"> | ||
| > {-# LANGUAGE OverloadedStrings #-} | ||
| > {-# OPTIONS_GHC -fno-warn-unused-do-bind #-} | ||
| > import Control.Applicative ((<$>), (<*>)) | ||
| > import Data.Maybe (isJust) | ||
|
|
||
| > import Data.Text (Text) | ||
| > import Text.Blaze ((!)) | ||
| > import qualified Data.Text as T | ||
| > import qualified Happstack.Server as Happstack | ||
| > import qualified Text.Blaze.Html5 as H | ||
| > import qualified Text.Blaze.Html5.Attributes as A | ||
|
|
||
| > import Text.Digestive | ||
| > import Text.Digestive.Blaze.Html5 | ||
| > import Text.Digestive.Happstack | ||
| > import Text.Digestive.Util | ||
|
|
||
| Simple forms and validation | ||
| --------------------------- | ||
|
|
||
| Let's start by creating a very simple datatype to represent a user: | ||
|
|
||
| > data User = User | ||
| > { userName :: Text | ||
| > , userMail :: Text | ||
| > } deriving (Show) | ||
|
|
||
| And dive in immediately to create a `Form` for a user. The `Form v m a` type | ||
| has three parameters: | ||
|
|
||
| - `v`: the type for messages and errors (usually a `String`-like type, `Text` in | ||
| this case); | ||
| - `m`: the monad we are operating in, not specified here; | ||
| - `a`: the return type of the `Form`, in this case, this is obviously `User`. | ||
|
|
||
| > userForm :: Monad m => Form Text m User | ||
|
|
||
| We create forms by using the `Applicative` interface. A few form types are | ||
| provided in the `Text.Digestive.Form` module, such as `text`, `string`, | ||
| `bool`... | ||
|
|
||
| In the `digestive-functors` library, the developer is required to label each | ||
| field using the `.:` operator. This might look like a bit of a burden, but it | ||
| allows you to do some really useful stuff, like separating the `Form` from the | ||
| actual HTML layout. | ||
|
|
||
| > userForm = User | ||
| > <$> "name" .: text Nothing | ||
| > <*> "mail" .: check "Not a valid email address" checkEmail (text Nothing) | ||
|
|
||
| The `check` function enables you to validate the result of a form. For example, | ||
| we can validate the email address with a really naive `checkEmail` function. | ||
|
|
||
| > checkEmail :: Text -> Bool | ||
| > checkEmail = isJust . T.find (== '@') | ||
|
|
||
| More validation | ||
| --------------- | ||
|
|
||
| For our example, we also want descriptions of Haskell libraries, and in order to | ||
| do that, we need package versions... | ||
|
|
||
| > type Version = [Int] | ||
|
|
||
| We want to let the user input a version number such as `0.1.0.0`. This means we | ||
| need to validate if the input `Text` is of this form, and then we need to parse | ||
| it to a `Version` type. Fortunately, we can do this in a single function: | ||
| `validate` allows conversion between values, which can optionally fail. | ||
|
|
||
| `readMaybe :: Read a => String -> Maybe a` is a utility function imported from | ||
| `Text.Digestive.Util`. | ||
|
|
||
| > validateVersion :: Text -> Result Text Version | ||
| > validateVersion = maybe (Error "Cannot parse version") Success . | ||
| > mapM (readMaybe . T.unpack) . T.split (== '.') | ||
|
|
||
| A quick test in GHCi: | ||
|
|
||
| ghci> validateVersion (T.pack "0.3.2.1") | ||
| Success [0,3,2,1] | ||
| ghci> validateVersion (T.pack "0.oops") | ||
| Error "Cannot parse version" | ||
|
|
||
| It works! This means we can now easily add a `Package` type and a `Form` for it: | ||
|
|
||
| > data Category = Web | Text | Math | ||
| > deriving (Bounded, Enum, Eq, Show) | ||
|
|
||
| > data Package = Package Text Version Category | ||
| > deriving (Show) | ||
|
|
||
| > packageForm :: Monad m => Form Text m Package | ||
| > packageForm = Package | ||
| > <$> "name" .: text Nothing | ||
| > <*> "version" .: validate validateVersion (text (Just "0.0.0.1")) | ||
| > <*> "category" .: choice categories Nothing | ||
| > where | ||
| > categories = [(x, T.pack (show x)) | x <- [minBound .. maxBound]] | ||
|
|
||
| Composing forms | ||
| --------------- | ||
|
|
||
| A release has an author and a package. Let's use this to illustrate the | ||
| composability of the digestive-functors library: we can reuse the forms we have | ||
| written earlier on. | ||
|
|
||
| > data Release = Release User Package | ||
| > deriving (Show) | ||
|
|
||
| > releaseForm :: Monad m => Form Text m Release | ||
| > releaseForm = Release | ||
| > <$> "author" .: userForm | ||
| > <*> "package" .: packageForm | ||
|
|
||
| Views | ||
| ----- | ||
|
|
||
| As mentioned before, one of the advantages of using digestive-functors is | ||
| separation of forms and their actual HTML layout. In order to do this, we have | ||
| another type, `View`. | ||
|
|
||
| We can get a `View` from a `Form` by supplying input. A `View` contains more | ||
| information than a `Form`, it has: | ||
|
|
||
| - the original form; | ||
| - the input given by the user; | ||
| - any errors that have occurred. | ||
|
|
||
| It is this view that we convert to HTML. For this tutorial, we use the | ||
| [blaze-html] library, and some helpers from the `digestive-functors-blaze` | ||
| library. | ||
|
|
||
| [blaze-html]: http://jaspervdj.be/blaze/ | ||
|
|
||
| Let's write a view for the `User` form. As you can see, we here refer to the | ||
| different fields in the `userForm`. The `errorList` will generate a list of | ||
| errors for the `"mail"` field. | ||
|
|
||
| > userView :: View H.Html -> H.Html | ||
| > userView view = do | ||
| > label "name" view "Name: " | ||
| > inputText "name" view | ||
| > H.br | ||
| > | ||
| > errorList "mail" view | ||
| > label "mail" view "Email address: " | ||
| > inputText "mail" view | ||
| > H.br | ||
|
|
||
| Like forms, views are also composable: let's illustrate that by adding a view | ||
| for the `releaseForm`, in which we reuse `userView`. In order to do this, we | ||
| take only the parts relevant to the author from the view by using `subView`. We | ||
| can then pass the resulting view to our own `userView`. | ||
| We have no special view code for `Package`, so we can just add that to | ||
| `releaseView` as well. `childErrorList` will generate a list of errors for each | ||
| child of the specified form. In this case, this means a list of errors from | ||
| `"package.name"` and `"package.version"`. Note how we use `foo.bar` to refer to | ||
| nested forms. | ||
|
|
||
| > releaseView :: View H.Html -> H.Html | ||
| > releaseView view = do | ||
| > H.h2 "Author" | ||
| > userView $ subView "author" view | ||
| > | ||
| > H.h2 "Package" | ||
| > childErrorList "package" view | ||
| > | ||
| > label "package.name" view "Name: " | ||
| > inputText "package.name" view | ||
| > H.br | ||
| > | ||
| > label "package.version" view "Version: " | ||
| > inputText "package.version" view | ||
| > H.br | ||
| > | ||
| > label "package.category" view "Category: " | ||
| > inputSelect "package.category" view | ||
| > H.br | ||
|
|
||
| The attentive reader might have wondered what the type parameter for `View` is: | ||
| it is the `String`-like type used for e.g. error messages. | ||
| But wait! We have | ||
| releaseForm :: Monad m => Form Text m Release | ||
| releaseView :: View H.Html -> H.Html | ||
| ... doesn't this mean that we need a `View Text` rather than a `View Html`? The | ||
| answer is yes -- but having `View Html` allows us to write these views more | ||
| easily with the `digestive-functors-blaze` library. Fortunately, we will be able | ||
| to fix this using the `Functor` instance of `View`. | ||
| fmap :: Monad m => (v -> w) -> View v -> View w | ||
| A backend | ||
| --------- | ||
| To finish this tutorial, we need to be able to actually run this code. We need | ||
| an HTTP server for that, and we use [Happstack] for this tutorial. The | ||
| `digestive-functors-happstack` library gives about everything we need for this. | ||
| [Happstack]: http://happstack.com/ | ||
|
|
||
| > site :: Happstack.ServerPart Happstack.Response | ||
| > site = do | ||
| > Happstack.decodeBody $ Happstack.defaultBodyPolicy "/tmp" 4096 4096 4096 | ||
| > r <- runForm "test" releaseForm | ||
| > case r of | ||
| > (view, Nothing) -> do | ||
| > let view' = fmap H.toHtml view | ||
| > Happstack.ok $ Happstack.toResponse $ | ||
| > template $ | ||
| > form view' "/" $ do | ||
| > releaseView view' | ||
| > H.br | ||
| > inputSubmit "Submit" | ||
| > (_, Just release) -> Happstack.ok $ Happstack.toResponse $ | ||
| > template $ do | ||
| > css | ||
| > H.h1 "Release received" | ||
| > H.p $ H.toHtml $ show release | ||
| > | ||
| > main :: IO () | ||
| > main = Happstack.simpleHTTP Happstack.nullConf site | ||
|
|
||
| Utilities | ||
| --------- | ||
|
|
||
| > template :: H.Html -> H.Html | ||
| > template body = H.docTypeHtml $ do | ||
| > H.head $ do | ||
| > H.title "digestive-functors tutorial" | ||
| > css | ||
| > H.body body | ||
| > css :: H.Html | ||
| > css = H.style ! A.type_ "text/css" $ do | ||
| > "label {width: 130px; float: left; clear: both}" | ||
| > "ul.digestive-functors-error-list {" | ||
| > " color: red;" | ||
| > " list-style-type: none;" | ||
| > " padding-left: 0px;" | ||
| > "}" | ||
| </textarea> | ||
| </form> | ||
|
|
||
| <p><strong>MIME types | ||
| defined:</strong> <code>text/x-literate-haskell</code>.</p> | ||
|
|
||
| <p>Parser configuration parameters recognized: <code>base</code> to | ||
| set the base mode (defaults to <code>"haskell"</code>).</p> | ||
|
|
||
| <script> | ||
| var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: "haskell-literate"}); | ||
| </script> | ||
|
|
||
| </article> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| <!doctype html> | ||
|
|
||
| <title>CodeMirror: JSX mode</title> | ||
| <meta charset="utf-8"/> | ||
| <link rel=stylesheet href="../../doc/docs.css"> | ||
|
|
||
| <link rel="stylesheet" href="../../lib/codemirror.css"> | ||
| <script src="../../lib/codemirror.js"></script> | ||
| <script src="../javascript/javascript.js"></script> | ||
| <script src="../xml/xml.js"></script> | ||
| <script src="jsx.js"></script> | ||
| <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style> | ||
| <div id=nav> | ||
| <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a> | ||
|
|
||
| <ul> | ||
| <li><a href="../../index.html">Home</a> | ||
| <li><a href="../../doc/manual.html">Manual</a> | ||
| <li><a href="https://github.com/codemirror/codemirror">Code</a> | ||
| </ul> | ||
| <ul> | ||
| <li><a href="../index.html">Language modes</a> | ||
| <li><a class=active href="#">JSX</a> | ||
| </ul> | ||
| </div> | ||
|
|
||
| <article> | ||
| <h2>JSX mode</h2> | ||
|
|
||
| <div><textarea id="code" name="code">// Code snippets from http://facebook.github.io/react/docs/jsx-in-depth.html | ||
|
|
||
| // Rendering HTML tags | ||
| var myDivElement = <div className="foo" />; | ||
| ReactDOM.render(myDivElement, document.getElementById('example')); | ||
|
|
||
| // Rendering React components | ||
| var MyComponent = React.createClass({/*...*/}); | ||
| var myElement = <MyComponent someProperty={true} />; | ||
| ReactDOM.render(myElement, document.getElementById('example')); | ||
|
|
||
| // Namespaced components | ||
| var Form = MyFormComponent; | ||
|
|
||
| var App = ( | ||
| <Form> | ||
| <Form.Row> | ||
| <Form.Label /> | ||
| <Form.Input /> | ||
| </Form.Row> | ||
| </Form> | ||
| ); | ||
|
|
||
| // Attribute JavaScript expressions | ||
| var person = <Person name={window.isLoggedIn ? window.name : ''} />; | ||
|
|
||
| // Boolean attributes | ||
| <input type="button" disabled />; | ||
| <input type="button" disabled={true} />; | ||
|
|
||
| // Child JavaScript expressions | ||
| var content = <Container>{window.isLoggedIn ? <Nav /> : <Login />}</Container>; | ||
|
|
||
| // Comments | ||
| var content = ( | ||
| <Nav> | ||
| {/* child comment, put {} around */} | ||
| <Person | ||
| /* multi | ||
| line | ||
| comment */ | ||
| name={window.isLoggedIn ? window.name : ''} // end of line comment | ||
| /> | ||
| </Nav> | ||
| ); | ||
| </textarea></div> | ||
|
|
||
| <script> | ||
| var editor = CodeMirror.fromTextArea(document.getElementById("code"), { | ||
| lineNumbers: true, | ||
| mode: "jsx" | ||
| }) | ||
| </script> | ||
|
|
||
| <p>JSX Mode for <a href="http://facebook.github.io/react">React</a>'s | ||
| JavaScript syntax extension.</p> | ||
|
|
||
| <p><strong>MIME types defined:</strong> <code>text/jsx</code>.</p> | ||
|
|
||
| </article> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| // CodeMirror, copyright (c) by Marijn Haverbeke and others | ||
| // Distributed under an MIT license: http://codemirror.net/LICENSE | ||
|
|
||
| (function(mod) { | ||
| if (typeof exports == "object" && typeof module == "object") // CommonJS | ||
| mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript")) | ||
| else if (typeof define == "function" && define.amd) // AMD | ||
| define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript"], mod) | ||
| else // Plain browser env | ||
| mod(CodeMirror) | ||
| })(function(CodeMirror) { | ||
| "use strict" | ||
|
|
||
| // Depth means the amount of open braces in JS context, in XML | ||
| // context 0 means not in tag, 1 means in tag, and 2 means in tag | ||
| // and js block comment. | ||
| function Context(state, mode, depth, prev) { | ||
| this.state = state; this.mode = mode; this.depth = depth; this.prev = prev | ||
| } | ||
|
|
||
| function copyContext(context) { | ||
| return new Context(CodeMirror.copyState(context.mode, context.state), | ||
| context.mode, | ||
| context.depth, | ||
| context.prev && copyContext(context.prev)) | ||
| } | ||
|
|
||
| CodeMirror.defineMode("jsx", function(config) { | ||
| var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false}) | ||
| var jsMode = CodeMirror.getMode(config, "javascript") | ||
|
|
||
| function flatXMLIndent(state) { | ||
| var tagName = state.tagName | ||
| state.tagName = null | ||
| var result = xmlMode.indent(state, "") | ||
| state.tagName = tagName | ||
| return result | ||
| } | ||
|
|
||
| function token(stream, state) { | ||
| if (state.context.mode == xmlMode) | ||
| return xmlToken(stream, state, state.context) | ||
| else | ||
| return jsToken(stream, state, state.context) | ||
| } | ||
|
|
||
| function xmlToken(stream, state, cx) { | ||
| if (cx.depth == 2) { // Inside a JS /* */ comment | ||
| if (stream.match(/^.*?\*\//)) cx.depth = 1 | ||
| else stream.skipToEnd() | ||
| return "comment" | ||
| } | ||
|
|
||
| if (stream.peek() == "{") { | ||
| xmlMode.skipAttribute(cx.state) | ||
|
|
||
| var indent = flatXMLIndent(cx.state), xmlContext = cx.state.context | ||
| // If JS starts on same line as tag | ||
| if (xmlContext && stream.match(/^[^>]*>\s*$/, false)) { | ||
| while (xmlContext.prev && !xmlContext.startOfLine) | ||
| xmlContext = xmlContext.prev | ||
| // If tag starts the line, use XML indentation level | ||
| if (xmlContext.startOfLine) indent -= config.indentUnit | ||
| // Else use JS indentation level | ||
| else if (cx.prev.state.lexical) indent = cx.prev.state.lexical.indented | ||
| // Else if inside of tag | ||
| } else if (cx.depth == 1) { | ||
| indent += config.indentUnit | ||
| } | ||
|
|
||
| state.context = new Context(CodeMirror.startState(jsMode, indent), | ||
| jsMode, 0, state.context) | ||
| return null | ||
| } | ||
|
|
||
| if (cx.depth == 1) { // Inside of tag | ||
| if (stream.peek() == "<") { // Tag inside of tag | ||
| xmlMode.skipAttribute(cx.state) | ||
| state.context = new Context(CodeMirror.startState(xmlMode, flatXMLIndent(cx.state)), | ||
| xmlMode, 0, state.context) | ||
| return null | ||
| } else if (stream.match("//")) { | ||
| stream.skipToEnd() | ||
| return "comment" | ||
| } else if (stream.match("/*")) { | ||
| cx.depth = 2 | ||
| return token(stream, state) | ||
| } | ||
| } | ||
|
|
||
| var style = xmlMode.token(stream, cx.state), cur = stream.current(), stop | ||
| if (/\btag\b/.test(style)) { | ||
| if (/>$/.test(cur)) { | ||
| if (cx.state.context) cx.depth = 0 | ||
| else state.context = state.context.prev | ||
| } else if (/^</.test(cur)) { | ||
| cx.depth = 1 | ||
| } | ||
| } else if (!style && (stop = cur.indexOf("{")) > -1) { | ||
| stream.backUp(cur.length - stop) | ||
| } | ||
| return style | ||
| } | ||
|
|
||
| function jsToken(stream, state, cx) { | ||
| if (stream.peek() == "<" && jsMode.expressionAllowed(stream, cx.state)) { | ||
| jsMode.skipExpression(cx.state) | ||
| state.context = new Context(CodeMirror.startState(xmlMode, jsMode.indent(cx.state, "")), | ||
| xmlMode, 0, state.context) | ||
| return null | ||
| } | ||
|
|
||
| var style = jsMode.token(stream, cx.state) | ||
| if (!style && cx.depth != null) { | ||
| var cur = stream.current() | ||
| if (cur == "{") { | ||
| cx.depth++ | ||
| } else if (cur == "}") { | ||
| if (--cx.depth == 0) state.context = state.context.prev | ||
| } | ||
| } | ||
| return style | ||
| } | ||
|
|
||
| return { | ||
| startState: function() { | ||
| return {context: new Context(CodeMirror.startState(jsMode), jsMode)} | ||
| }, | ||
|
|
||
| copyState: function(state) { | ||
| return {context: copyContext(state.context)} | ||
| }, | ||
|
|
||
| token: token, | ||
|
|
||
| indent: function(state, textAfter, fullLine) { | ||
| return state.context.mode.indent(state.context.state, textAfter, fullLine) | ||
| }, | ||
|
|
||
| innerMode: function(state) { | ||
| return state.context | ||
| } | ||
| } | ||
| }, "xml", "javascript") | ||
|
|
||
| CodeMirror.defineMIME("text/jsx", "jsx") | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| // CodeMirror, copyright (c) by Marijn Haverbeke and others | ||
| // Distributed under an MIT license: http://codemirror.net/LICENSE | ||
|
|
||
| (function() { | ||
| var mode = CodeMirror.getMode({indentUnit: 2}, "jsx") | ||
| function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)) } | ||
|
|
||
| MT("selfclose", | ||
| "[keyword var] [def x] [operator =] [bracket&tag <] [tag foo] [bracket&tag />] [operator +] [number 1];") | ||
|
|
||
| MT("openclose", | ||
| "([bracket&tag <][tag foo][bracket&tag >]hello [atom &][bracket&tag </][tag foo][bracket&tag >][operator ++])") | ||
|
|
||
| MT("attr", | ||
| "([bracket&tag <][tag foo] [attribute abc]=[string 'value'][bracket&tag >]hello [atom &][bracket&tag </][tag foo][bracket&tag >][operator ++])") | ||
|
|
||
| MT("braced_attr", | ||
| "([bracket&tag <][tag foo] [attribute abc]={[number 10]}[bracket&tag >]hello [atom &][bracket&tag </][tag foo][bracket&tag >][operator ++])") | ||
|
|
||
| MT("braced_text", | ||
| "([bracket&tag <][tag foo][bracket&tag >]hello {[number 10]} [atom &][bracket&tag </][tag foo][bracket&tag >][operator ++])") | ||
|
|
||
| MT("nested_tag", | ||
| "([bracket&tag <][tag foo][bracket&tag ><][tag bar][bracket&tag ></][tag bar][bracket&tag ></][tag foo][bracket&tag >][operator ++])") | ||
|
|
||
| MT("nested_jsx", | ||
| "[keyword return] (", | ||
| " [bracket&tag <][tag foo][bracket&tag >]", | ||
| " say {[number 1] [operator +] [bracket&tag <][tag bar] [attribute attr]={[number 10]}[bracket&tag />]}!", | ||
| " [bracket&tag </][tag foo][bracket&tag >][operator ++]", | ||
| ")") | ||
|
|
||
| MT("preserve_js_context", | ||
| "[variable x] [operator =] [string-2 `quasi${][bracket&tag <][tag foo][bracket&tag />][string-2 }quoted`]") | ||
|
|
||
| MT("line_comment", | ||
| "([bracket&tag <][tag foo] [comment // hello]", | ||
| " [bracket&tag ></][tag foo][bracket&tag >][operator ++])") | ||
|
|
||
| MT("line_comment_not_in_tag", | ||
| "([bracket&tag <][tag foo][bracket&tag >] // hello", | ||
| " [bracket&tag </][tag foo][bracket&tag >][operator ++])") | ||
|
|
||
| MT("block_comment", | ||
| "([bracket&tag <][tag foo] [comment /* hello]", | ||
| "[comment line 2]", | ||
| "[comment line 3 */] [bracket&tag ></][tag foo][bracket&tag >][operator ++])") | ||
|
|
||
| MT("block_comment_not_in_tag", | ||
| "([bracket&tag <][tag foo][bracket&tag >]/* hello", | ||
| " line 2", | ||
| " line 3 */ [bracket&tag </][tag foo][bracket&tag >][operator ++])") | ||
|
|
||
| MT("missing_attr", | ||
| "([bracket&tag <][tag foo] [attribute selected][bracket&tag />][operator ++])") | ||
|
|
||
| MT("indent_js", | ||
| "([bracket&tag <][tag foo][bracket&tag >]", | ||
| " [bracket&tag <][tag bar] [attribute baz]={[keyword function]() {", | ||
| " [keyword return] [number 10]", | ||
| " }}[bracket&tag />]", | ||
| " [bracket&tag </][tag foo][bracket&tag >])") | ||
|
|
||
| MT("spread", | ||
| "([bracket&tag <][tag foo] [attribute bar]={[meta ...][variable baz] [operator /][number 2]}[bracket&tag />])") | ||
|
|
||
| MT("tag_attribute", | ||
| "([bracket&tag <][tag foo] [attribute bar]=[bracket&tag <][tag foo][bracket&tag />/>][operator ++])") | ||
| })() |