git.iko.soy/rowtype-yoga/psfp
on master help sign in
Add syntax highlighting and border for Editor
d8d1ae7Mark Eibes
src/Monaco/Editor.js+182
@@ -31,6 +31,24 @@ exports.setThemeImpl = function(monacoInstance) {
31 }
31 }
32}
32}
33
33
34+exports.registerLanguageImpl = function(monacoInstance) {
35+ return function(languageId) {
36+ return function() {
37+ monacoInstance.languages.register({ id: languageId})
38+ }
39+ }
40+}
41+
42+exports.setMonarchTokensProviderImpl = function(monacoInstance) {
43+ return function(name) {
44+ return function(monarchLanguage) {
45+ return function() {
46+ return monacoInstance.languages.setMonarchTokensProvider(name, monarchLanguage);
47+ }
48+ }
49+ }
50+}
51+
34exports.nightOwlTheme = {
52exports.nightOwlTheme = {
35 "base": "vs-dark",
53 "base": "vs-dark",
36 "inherit": true,
54 "inherit": true,
@@ -708,3 +726,167 @@ exports.nightOwlTheme = {
708 }
726 }
709}
727}
710
728
729+exports.purescriptSyntax = {
730+ "displayName": "Purescript",
731+ "name": "purescrript",
732+ "mimeTypes": ["text/purescript"],
733+ "fileExtensions": ["purs"],
734+
735+ "editorOptions": { "tabSize": 2, "insertSpaces": true },
736+
737+ "lineComment": "--",
738+ "blockCommentStart": "{-",
739+ "blockCommentEnd": "-}",
740+
741+ "keywords": [
742+ "case", "class", "data", "derive", "do", "else",
743+ "if", "import", "in", "infix", "infixl", "infixr", "instance",
744+ "let", "module", "newtype", "of", "then", "type", "where",
745+ "->", "|", "=>", "::", "\\", "=", "|", "foreign", "forall", "∷"
746+ ],
747+
748+ "extraKeywords": [
749+ ],
750+
751+ "typeKeywords": [
752+ ],
753+
754+ "dataStart": [
755+ "^data"
756+ ],
757+
758+ "typeStart": [
759+ "class","instance","type", "derive"
760+ ],
761+
762+
763+ "escapes" : "\\\\(?:[nrt\\\\\"'\\?]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{6})",
764+ "symbols0": "[\\$%&\\*\\+@!\\/\\\\\\^~=\\.:\\-\\?]",
765+ "symbols" : "(?:@symbols0|[\\|<>])+",
766+ "idchars" : "(?:[\\w'])*",
767+
768+ "tokenizer": {
769+ "root": [
770+
771+ ["((?:[A-Z]@idchars\\.)+)([a-z]@idchars)", ["identifier.namespace","identifier"]],
772+ ["((?:[A-Z]@idchars\\.)+)([A-Z]@idchars)", ["identifier.namespace","constructor"]],
773+
774+ ["[a-z]@idchars(?!\\.)",
775+ { "cases":{ "@dataStart": { "token": "keyword", "next" : "@type_data" },
776+ "@typeStart": { "token": "keyword", "next" : "@type_type" },
777+ "@keywords": "keyword",
778+ "@extraKeywords": "keyword",
779+ "@default" : "identifier" }
780+ }
781+ ],
782+
783+ ["[A-Z]@idchars", "type.identifier" ],
784+
785+ ["_@idchars", "identifier.wildcard"],
786+
787+
788+ ["([a-z]@idchars\\.)+", "identifier.namespace" ],
789+
790+
791+ { "include": "@whitespace" },
792+
793+ ["[{}()\\[\\]]", "@brackets"],
794+ ["[,`]", "delimiter"],
795+
796+
797+ ["::(?!@symbols0)", "type.operator", "@type_line"],
798+ ["@symbols", { "cases": { "@keywords": "keyword.operator",
799+ "@extraKeywords": "keyword.operator",
800+ "@default": "operator" }}
801+ ],
802+
803+
804+ ["[0-9]+\\.[0-9]+([eE][\\-+]?[0-9]+)?", "number.float"],
805+ ["0[xX][0-9a-fA-F]+", "number.hex"],
806+ ["[0-9]+", "number"],
807+
808+
809+ ["\"([^\"\\\\]|\\\\.)*$", "string.invalid" ],
810+ ["\"", { "token": "string.quote", "bracket": "@open", "next": "@string" } ],
811+
812+
813+ ["'[^\\\\']'", "string"],
814+ ["(')(@escapes)(')", ["string","string.escape","string"]],
815+ ["'", "string.invalid"],
816+
817+
818+ ["^[ ]*#.*", "namespace"]
819+ ],
820+
821+ "whitespace": [
822+ ["[ \\r\\n]+", ""],
823+ ["{-", "comment", "@comment" ],
824+ ["--.*$", "comment"]
825+ ],
826+
827+ "comment": [
828+ ["[^\\{\\-]+", "comment" ],
829+ ["{-", "comment", "@push" ],
830+ ["-}", "comment", "@pop" ],
831+ ["[\\{\\-]", "comment"]
832+ ],
833+
834+ "string": [
835+ ["[^\\\\\"]+", "string"],
836+ ["@escapes", "string.escape"],
837+ ["\\\\.", "string.escape.invalid"],
838+ ["\"", { "token": "string.quote", "bracket": "@close", "next": "@pop" } ]
839+ ],
840+
841+ "type_type": [
842+ [ "\\=", "keyword"],
843+ ["^\\s*(?!\\-\\-|\\{\\-)[^\\s]", { "token": "@rematch", "next": "@pop" } ],
844+ { "include": "@type" }
845+ ],
846+
847+ "type_data": [
848+ [ "([=|])(\\s*)([A-Z]@idchars)", ["keyword.operator","","constructor"]],
849+ ["^\\s*$", { "token": "", "next": "@pop" } ],
850+ { "include": "@type" }
851+ ],
852+
853+ "type_line": [
854+ ["^\\s*(?!\\-\\-|\\{\\-)[_a-z]", { "token": "@rematch", "next": "@pop" } ],
855+ ["[^=]*=", { "token": "@rematch", "next": "@pop" } ],
856+ { "include": "@type" }
857+ ],
858+
859+ "type": [
860+ [ "[(\\[]", { "token": "@brackets.type" }, "@type_nested"],
861+ { "include": "@type_content" }
862+ ],
863+
864+ "type_nested": [
865+ ["[)\\]]", { "token": "@brackets.type" }, "@pop" ],
866+ ["[(\\[]", { "token": "@brackets.type" }, "@push"],
867+ [",", "delimiter.type"],
868+ { "include": "@type_content" }
869+ ],
870+
871+ "type_content": [
872+ { "include": "@whitespace" },
873+
874+
875+ ["[a-z]\\d*\\b", "type.identifier.typevar"],
876+ ["_@idchars", "type.identifier.typevar"],
877+
878+ ["((?:[A-Z]@idchars\\.)*)([A-Z]@idchars)",
879+ ["type.identifier.namespace","type.identifier"]
880+ ],
881+ ["((?:[A-Z]@idchars\\.)*)([a-z]@idchars)",
882+ { "cases": { "@typeKeywords": ["type.identifier.namespace","type.keyword"],
883+ "@keywords": { "token": "@rematch", "next": "@pop" },
884+ "@default": ["type.identifier.namespace","type.identifier"]
885+ }}
886+ ],
887+
888+ ["::|->|[\\.:|]", "type.operator"],
889+ ["[\\s\\S]","@rematch","@pop"]
890+ ]
891+ }
892+ }
src/Monaco/Editor.purs+61−37
@@ -1,28 +1,26 @@
1module Editor where
1module Editor where
2
2
3import Prelude
3import Prelude
4−
4+import CSS.Safer (cssSafer)
5import Control.Promise (Promise)
5import Control.Promise (Promise)
6import Control.Promise as Promise
6import Control.Promise as Promise
7−import Data.Foldable (for_)
8import Data.Maybe (Maybe(..))
7import Data.Maybe (Maybe(..))
9import Data.Nullable (notNull, null)
8import Data.Nullable (notNull, null)
10−import Data.Tuple.Nested ((/\))
11−import Debug.Trace (spy)
12import Effect (Effect)
9import Effect (Effect)
13import Effect.Aff (Aff)
10import Effect.Aff (Aff)
14import Effect.Class (liftEffect)
11import Effect.Class (liftEffect)
15import Effect.Class.Console (warn)
12import Effect.Class.Console (warn)
16−import Effect.Uncurried (EffectFn1, EffectFn2, mkEffectFn1, mkEffectFn2, runEffectFn1)
13+import Effect.Uncurried (EffectFn2, mkEffectFn2)
17−import Foreign (Foreign)
14+import Foreign (Foreign, unsafeToForeign)
18import Prim.Row (class Union)
15import Prim.Row (class Union)
19import React.Basic (JSX, ReactComponent, element, fragment)
16import React.Basic (JSX, ReactComponent, element, fragment)
20import React.Basic.DOM as R
17import React.Basic.DOM as R
21import React.Basic.Events (handler_)
18import React.Basic.Events (handler_)
22−import React.Basic.Hooks (component, readRefMaybe, useRef, useState, writeRef)
19+import React.Basic.Hooks (component, readRefMaybe, useRef, writeRef)
23import React.Basic.Hooks as React
20import React.Basic.Hooks as React
24import React.Basic.Hooks.Aff (useAff)
21import React.Basic.Hooks.Aff (useAff)
25−import Simple.JSON as Foreign
22+import React.Helpers (wrapperDiv)
23+import Theme.Styles (makeStyles_)
26import Web.DOM (Node)
24import Web.DOM (Node)
27
25
28type EditorProps
26type EditorProps
@@ -62,46 +60,72 @@ editor = editorImpl
62initMonaco ∷ Aff Monaco
60initMonaco ∷ Aff Monaco
63initMonaco = liftEffect initMonacoImpl >>= Promise.toAff
61initMonaco = liftEffect initMonacoImpl >>= Promise.toAff
64
62
63+themeName ∷ String
65themeName = "NightOwl"
64themeName = "NightOwl"
66
65
66+foreign import data MonarchLanguage ∷ Type
67+
68+foreign import purescriptSyntax ∷ MonarchLanguage
69+
70+foreign import registerLanguageImpl ∷ Monaco -> String -> Effect Unit
71+
72+foreign import setMonarchTokensProviderImpl ∷ Monaco -> String -> MonarchLanguage -> Effect Unit
73+
67mkEditor ∷ Effect (ReactComponent {})
74mkEditor ∷ Effect (ReactComponent {})
68mkEditor = do
75mkEditor = do
76+ useStyles <-
77+ makeStyles_
78+ { wrapper:
79+ cssSafer
80+ { margin: "0"
81+ , boxSizing: "border-box"
82+ , padding: "35px 40px"
83+ , width: "100%"
84+ , height: "100%"
85+ , borderRadius: "32px"
86+ , backgroundColor: "#011627"
87+ }
88+ }
69 component "Editor" \{} -> React.do
89 component "Editor" \{} -> React.do
90+ classes <- useStyles
70 ref <- useRef null
91 ref <- useRef null
71 useAff unit
92 useAff unit
72 $ do
93 $ do
73 monaco <- initMonaco
94 monaco <- initMonaco
74 defineThemeImpl monaco themeName nightOwlTheme # liftEffect
95 defineThemeImpl monaco themeName nightOwlTheme # liftEffect
75 setThemeImpl monaco themeName # liftEffect
96 setThemeImpl monaco themeName # liftEffect
97+ registerLanguageImpl monaco "purescript" # liftEffect
98+ setMonarchTokensProviderImpl monaco "purescript" purescriptSyntax # liftEffect
76 pure
99 pure
77 $ fragment
100 $ fragment
101+ [ R.button
102+ { onClick:
78− [ R.button { onClick: handler_ do
103+ handler_ do
79− maybeEditor <- readRefMaybe ref
104+ maybeEditor <- readRefMaybe ref
80− case maybeEditor of
105+ case maybeEditor of
81− Nothing -> pure unit
106+ Nothing -> pure unit
82− Just ed -> do
107+ Just ed -> do
83− v <- getValue ed
108+ v <- getValue ed
84− warn $ "Value: " <> v
109+ warn $ "Value: " <> v
85− }
86− , element editor
87− { theme: themeName
88− , options:
89− Foreign.write
90− { fontFamily: "PragmataPro"
91− , fontLigatures: true
92− , fontSize: "16pt"
93− , lineNumbers: "off"
94− , glyphMargin: false
95− , folding: false
96− , lineDecorationsWidth: 0
97− , lineNumbersMinChars: 0
98− , minimap: { enabled: false }
99 }
110 }
100− , language: "haskell"
111+ , wrapperDiv { className: classes.wrapper }
101− -- [TODO] :(
112+ $ element editor
113+ { theme: themeName
114+ , options:
115+ unsafeToForeign
116+ { fontFamily: "PragmataPro"
117+ , fontLigatures: true
118+ , fontSize: "16pt"
119+ , lineNumbers: "off"
120+ , glyphMargin: false
121+ , folding: false
122+ , lineDecorationsWidth: 0
123+ , lineNumbersMinChars: 0
124+ , minimap: { enabled: false }
125+ }
126+ , language: "purescript"
102− -- https://microsoft.github.io/monaco-editor/playground.html#extending-language-services-custom-languages
127+ -- https://microsoft.github.io/monaco-editor/playground.html#extending-language-services-custom-languages
103− , editorDidMount:
128+ , editorDidMount:
104− mkEffectFn2 \node editor ->
105− (editor # notNull # writeRef ref)
129+ mkEffectFn2 \_ -> notNull >>> writeRef ref
106− }
130+ }
107− ]
131+ ]
src/React/Helpers.pursnew file+157
@@ -0,0 +1,157 @@
1+module React.Helpers where
2+
3+import Prelude
4+
5+import Data.Nullable (Nullable)
6+import Data.Symbol (SProxy(..))
7+import Foreign.Object (Object)
8+import Prim.Row (class Lacks, class Union)
9+import React.Basic (JSX)
10+import React.Basic.DOM (CSS)
11+import React.Basic.DOM as R
12+import React.Basic.Events (EventHandler)
13+import React.Basic.Hooks (Ref)
14+import Record (insert)
15+import Web.DOM (Node)
16+
17+wrapperDiv ∷ ∀ attrs attrs_.
18+ Lacks "children" attrs =>
19+ Union attrs attrs_ Props_div_no_children =>
20+ {|attrs}
21+ -> JSX
22+ -> JSX
23+wrapperDiv props child =
24+ R.div $ insert (SProxy ∷ _ "children") [child] props
25+
26+type Props_div_no_children =
27+ ( _data ∷ Object String
28+ , about ∷ String
29+ , acceptCharset ∷ String
30+ , accessKey ∷ String
31+ , allowFullScreen ∷ Boolean
32+ , allowTransparency ∷ Boolean
33+ , autoComplete ∷ Boolean
34+ , autoFocus ∷ Boolean
35+ , autoPlay ∷ Boolean
36+ , capture ∷ Boolean
37+ , cellPadding ∷ String
38+ , cellSpacing ∷ String
39+ , charSet ∷ String
40+ , classID ∷ String
41+ , className ∷ String
42+ , colSpan ∷ Int
43+ , contentEditable ∷ Boolean
44+ , contextMenu ∷ String
45+ , crossOrigin ∷ String
46+ , dangerouslySetInnerHTML ∷ { __html ∷ String }
47+ , datatype ∷ String
48+ , dateTime ∷ String
49+ , dir ∷ String
50+ , draggable ∷ Boolean
51+ , encType ∷ String
52+ , formAction ∷ String
53+ , formEncType ∷ String
54+ , formMethod ∷ String
55+ , formNoValidate ∷ Boolean
56+ , formTarget ∷ String
57+ , frameBorder ∷ String
58+ , hidden ∷ Boolean
59+ , hrefLang ∷ String
60+ , htmlFor ∷ String
61+ , httpEquiv ∷ String
62+ , icon ∷ String
63+ , id ∷ String
64+ , inlist ∷ String
65+ , inputMode ∷ String
66+ , is ∷ String
67+ , itemID ∷ String
68+ , itemProp ∷ String
69+ , itemRef ∷ String
70+ , itemScope ∷ Boolean
71+ , itemType ∷ String
72+ , key ∷ String
73+ , keyParams ∷ String
74+ , keyType ∷ String
75+ , lang ∷ String
76+ , marginHeight ∷ String
77+ , marginWidth ∷ String
78+ , maxLength ∷ Int
79+ , mediaGroup ∷ String
80+ , minLength ∷ Int
81+ , noValidate ∷ Boolean
82+ , onAnimationEnd ∷ EventHandler
83+ , onAnimationIteration ∷ EventHandler
84+ , onAnimationStart ∷ EventHandler
85+ , onBlur ∷ EventHandler
86+ , onClick ∷ EventHandler
87+ , onCompositionEnd ∷ EventHandler
88+ , onCompositionStart ∷ EventHandler
89+ , onCompositionUpdate ∷ EventHandler
90+ , onContextMenu ∷ EventHandler
91+ , onCopy ∷ EventHandler
92+ , onCut ∷ EventHandler
93+ , onDoubleClick ∷ EventHandler
94+ , onDrag ∷ EventHandler
95+ , onDragEnd ∷ EventHandler
96+ , onDragEnter ∷ EventHandler
97+ , onDragExit ∷ EventHandler
98+ , onDragLeave ∷ EventHandler
99+ , onDragOver ∷ EventHandler
100+ , onDragStart ∷ EventHandler
101+ , onDrop ∷ EventHandler
102+ , onFocus ∷ EventHandler
103+ , onGotPointerCapture ∷ EventHandler
104+ , onInvalid ∷ EventHandler
105+ , onKeyDown ∷ EventHandler
106+ , onKeyPress ∷ EventHandler
107+ , onKeyUp ∷ EventHandler
108+ , onLostPointerCapture ∷ EventHandler
109+ , onMouseDown ∷ EventHandler
110+ , onMouseEnter ∷ EventHandler
111+ , onMouseLeave ∷ EventHandler
112+ , onMouseMove ∷ EventHandler
113+ , onMouseOut ∷ EventHandler
114+ , onMouseOver ∷ EventHandler
115+ , onMouseUp ∷ EventHandler
116+ , onPaste ∷ EventHandler
117+ , onPointerCancel ∷ EventHandler
118+ , onPointerDown ∷ EventHandler
119+ , onPointerEnter ∷ EventHandler
120+ , onPointerLeave ∷ EventHandler
121+ , onPointerMove ∷ EventHandler
122+ , onPointerOut ∷ EventHandler
123+ , onPointerOver ∷ EventHandler
124+ , onPointerUp ∷ EventHandler
125+ , onSelect ∷ EventHandler
126+ , onSubmit ∷ EventHandler
127+ , onTouchCancel ∷ EventHandler
128+ , onTouchEnd ∷ EventHandler
129+ , onTouchMove ∷ EventHandler
130+ , onTouchStart ∷ EventHandler
131+ , onTransitionEnd ∷ EventHandler
132+ , onWheel ∷ EventHandler
133+ , prefix ∷ String
134+ , property ∷ String
135+ , radioGroup ∷ String
136+ , readOnly ∷ Boolean
137+ , ref ∷ Ref (Nullable Node)
138+ , resource ∷ String
139+ , role ∷ String
140+ , rowSpan ∷ Int
141+ , scoped ∷ Boolean
142+ , seamless ∷ Boolean
143+ , security ∷ String
144+ , spellCheck ∷ Boolean
145+ , srcDoc ∷ JSX
146+ , srcLang ∷ String
147+ , srcSet ∷ String
148+ , style ∷ CSS
149+ , suppressContentEditableWarning ∷ Boolean
150+ , tabIndex ∷ Int
151+ , title ∷ String
152+ , typeof ∷ String
153+ , unselectable ∷ Boolean
154+ , useMap ∷ String
155+ , vocab ∷ String
156+ , wmode ∷ String
157+ )