Commit e127d701 authored by Przemyslaw Kaminski's avatar Przemyslaw Kaminski

[CodeEditor] syntax highlighting with textarea overlay

parent 644f0001
...@@ -9,12 +9,55 @@ ...@@ -9,12 +9,55 @@
} }
.code-editor .editor .code { .code-editor .editor .code {
flex-grow: 1; flex-grow: 1;
height: 500px;
position: relative;
font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace;
font-size: 12px;
font-variant-ligatures: common-ligatures;
line-height: 1.5;
}
.code-editor .editor .code textarea {
border: 0px;
margin: 0px;
padding: 10px;
position: absolute;
left: 0px;
top: 0px;
resize: none;
height: 100%;
width: 100%;
overflow: hidden;
-webkit-text-fill-color: transparent;
font-family: inherit;
font-size: inherit;
font-style: inherit;
font-variant-ligatures: inherit;
font-weight: inherit;
letter-spacing: inherit;
line-height: inherit;
text-indent: inherit;
text-rendering: inherit;
text-transform: inherit;
} }
.code-editor .editor .code code { .code-editor .editor .code code {
background-color: #f7f7f9; background: rgba(0, 0, 0, 0) none repeat scroll 0% 0%;
border: 0px none;
margin: 0px;
padding: 10px;
color: #000; color: #000;
display: block; display: block;
height: 500px; pointer-events: none;
position: relative;
font-family: inherit;
font-size: inherit;
font-style: inherit;
font-variant-ligatures: inherit;
font-weight: inherit;
letter-spacing: inherit;
line-height: inherit;
text-indent: inherit;
text-rendering: inherit;
text-transform: inherit;
} }
.code-editor .editor .v-divider { .code-editor .editor .v-divider {
border-left: 1px solid gray; border-left: 1px solid gray;
......
@mixin font-inherit()
font-family: inherit
font-size: inherit
font-style: inherit
font-variant-ligatures: inherit
font-weight: inherit
letter-spacing: inherit
line-height: inherit
text-indent: inherit
text-rendering: inherit
text-transform: inherit
.code-editor .code-editor
.toolbar .toolbar
display: flex display: flex
...@@ -8,11 +20,35 @@ ...@@ -8,11 +20,35 @@
width: 100% width: 100%
.code .code
flex-grow: 1 flex-grow: 1
height: 500px
position: relative
font-family: Fira code,Fira Mono,Consolas,Menlo,Courier,monospace
font-size: 12px
font-variant-ligatures: common-ligatures
line-height: 1.5
textarea
border: 0px
margin: 0px
padding: 10px
position: absolute
left: 0px
top: 0px
resize: none
height: 100%
width: 100%
overflow: hidden
-webkit-text-fill-color: transparent
@include font-inherit()
code code
background-color: #f7f7f9 background: rgba(0, 0, 0, 0) none repeat scroll 0% 0%
border: 0px none
margin: 0px
padding: 10px
color: #000 color: #000
display: block display: block
height: 500px pointer-events: none
position: relative
@include font-inherit()
.v-divider .v-divider
border-left: 1px solid gray border-left: 1px solid gray
cursor: sw-resize cursor: sw-resize
......
...@@ -21,6 +21,7 @@ import Text.Markdown.SlamDown.Syntax (SlamDownP) ...@@ -21,6 +21,7 @@ import Text.Markdown.SlamDown.Syntax (SlamDownP)
import Text.Smolder.Renderer.String (render) import Text.Smolder.Renderer.String (render)
import Gargantext.Prelude import Gargantext.Prelude
import Gargantext.Utils.HighlightJS as HLJS
import Gargantext.Utils.Reactix as R2 import Gargantext.Utils.Reactix as R2
type Code = String type Code = String
...@@ -73,48 +74,52 @@ codeEditorCpt :: R.Component Props ...@@ -73,48 +74,52 @@ codeEditorCpt :: R.Component Props
codeEditorCpt = R.hooksComponent "G.C.CodeEditor" cpt codeEditorCpt = R.hooksComponent "G.C.CodeEditor" cpt
where where
cpt {code, defaultCodeType, onChange} _ = do cpt {code, defaultCodeType, onChange} _ = do
htmlRef <- R.useRef null controls <- initControls code defaultCodeType
codeRef <- R.useRef null
editorCodeRef <- R.useRef code
codeType <- R.useState' defaultCodeType
error <- R.useState' Nothing
viewType <- R.useState' Both
-- Initial rendering of elements with given data -- Initial rendering of elements with given data
-- Note: delay is necessary here, otherwise initially the HTML won't get -- Note: delay is necessary here, otherwise initially the HTML won't get
-- rendered (mDiv is still null) -- rendered (mDiv is still null)
R.useEffectOnce $ delay unit $ \_ -> do R.useEffectOnce $ delay unit $ \_ -> do
_ <- renderHtml (fst codeType) code htmlRef error _ <- renderHtml code controls
pure $ pure unit pure $ pure unit
R.useEffectOnce $ delay unit $ \_ -> do R.useEffectOnce $ delay unit $ \_ -> do
let mCodeEl = toMaybe $ R.readRef codeRef let mCodeOverlayEl = toMaybe $ R.readRef controls.codeOverlayElRef
case mCodeEl of case mCodeOverlayEl of
Nothing -> pure $ pure unit Nothing -> pure $ pure unit
Just codeEl -> do Just codeOverlayEl -> do
_ <- pure $ (codeEl .= "innerText") code _ <- pure $ (codeOverlayEl .= "innerText") code
HLJS.highlightBlock codeOverlayEl
pure $ pure unit pure $ pure unit
pure $ H.div { className: "code-editor" } [ pure $ H.div { className: "code-editor" } [
H.div { className: "row toolbar" } [ H.div { className: "row toolbar" } [
codeTypeSelector {codeType, onChange: onChangeCodeType editorCodeRef htmlRef error} codeTypeSelector {
, viewTypeSelector {state: viewType} codeType: controls.codeType
, onChange: onChangeCodeType controls
}
, viewTypeSelector {state: controls.viewType}
] ]
, H.div { className: "row error" } [ , H.div { className: "row error" } [
errorComponent {error} errorComponent {error: controls.error}
] ]
, H.div { className: "row editor" } [ , H.div { className: "row editor" } [
H.div { className: "code " <> (codeHidden $ fst viewType) } [ H.div { className: "code " <> (codeHidden $ fst controls.viewType) } [
H.code { className: "" H.textarea { defaultValue: code
, contentEditable: "true" , on: { change: onEditChange controls }
, ref: codeRef , ref: controls.codeElRef } [ ]
, H.code { className: ""
-- , contentEditable: "true"
, ref: controls.codeOverlayElRef
, rows: 30 , rows: 30
, on: { input: onEditChange (fst codeType) codeRef htmlRef editorCodeRef error } --, on: { input: onEditChange (fst codeType) codeElRef htmlRef editorCodeRef error }
} [] } []
] ]
, H.div { className: "v-divider " <> (dividerHidden $ fst viewType) } [ H.text " " ] , H.div { className: "v-divider " <> (dividerHidden $ fst controls.viewType) } [ H.text " " ]
, H.div { ref: htmlRef, className: "html " <> (previewHidden $ fst viewType) } [] , H.div { className: "html " <> (previewHidden $ fst controls.viewType)
, ref: controls.htmlElRef
} []
] ]
] ]
...@@ -133,34 +138,42 @@ codeEditorCpt = R.hooksComponent "G.C.CodeEditor" cpt ...@@ -133,34 +138,42 @@ codeEditorCpt = R.hooksComponent "G.C.CodeEditor" cpt
previewHidden _ = "hidden" previewHidden _ = "hidden"
-- Handle rerendering of preview when viewType changed -- Handle rerendering of preview when viewType changed
onChangeCodeType :: R.Ref String -> R.Ref (Nullable Element) -> R.State (Maybe Error) -> CodeType -> Effect Unit onChangeCodeType :: forall e. Record Controls -> e -> Effect Unit
onChangeCodeType editorCodeRef htmlRef error codeType = do onChangeCodeType controls _ = do
_ <- renderHtml codeType (R.readRef editorCodeRef) htmlRef error _ <- renderHtml (R.readRef controls.editorCodeRef) controls
pure unit pure unit
onEditChange :: forall e. CodeType -> R.Ref (Nullable Element) -> R.Ref (Nullable Element) -> R.Ref String -> R.State (Maybe Error) -> e -> Effect Unit onEditChange :: forall e. Record Controls -> e -> Effect Unit
onEditChange codeType codeRef htmlRef editorCodeRef error e = do onEditChange controls@{codeElRef, codeOverlayElRef, editorCodeRef} e = do
log2 "[onChange] e" e log2 "[onChange] e" e
let mCode = toMaybe $ R.readRef codeRef let mCodeOverlayEl = toMaybe $ R.readRef codeOverlayElRef
case mCode of case mCodeOverlayEl of
Nothing -> log "[onChange] mCode = Nothing" Nothing -> log "[onChange] mCodeOverlayEl = Nothing"
Just code -> do Just codeOverlayEl -> do
R.setRef editorCodeRef $ R2.innerText code --R.setRef editorCodeRef $ R2.innerText codeOverlayEl
pure unit let code = R2.unsafeEventValue e
renderHtml codeType (R.readRef editorCodeRef) htmlRef error R.setRef editorCodeRef code
_ <- case (toMaybe $ R.readRef codeElRef) of
renderHtml :: CodeType -> Code -> R.Ref (Nullable Element) -> R.State (Maybe Error) -> Effect Unit Nothing -> pure unit
renderHtml codeType code htmlRef (_ /\ setError) = Just codeEl -> do
case (toMaybe $ R.readRef htmlRef) of _ <- pure $ (codeOverlayEl .= "innerText") code
Nothing -> pure unit HLJS.highlightBlock codeOverlayEl
Just htmlEl -> do
case compile codeType code of
Left err -> do
setError $ const $ Just err
Right compiled -> do
setError $ const Nothing
_ <- pure $ (htmlEl .= "innerHTML") compiled
pure unit pure unit
pure unit
renderHtml (R.readRef controls.editorCodeRef) controls
renderHtml :: Code -> Record Controls -> Effect Unit
renderHtml code {codeType: (codeType /\ _), htmlElRef, error: (_ /\ setError)} =
case (toMaybe $ R.readRef htmlElRef) of
Nothing -> pure unit
Just htmlEl -> do
case compile codeType code of
Left err -> do
setError $ const $ Just err
Right compiled -> do
setError $ const Nothing
_ <- pure $ (htmlEl .= "innerHTML") compiled
pure unit
type ErrorComponentProps = type ErrorComponentProps =
...@@ -247,3 +260,34 @@ viewTypeSelectorCpt = R.hooksComponent "G.C.ViewTypeSelector" cpt ...@@ -247,3 +260,34 @@ viewTypeSelectorCpt = R.hooksComponent "G.C.ViewTypeSelector" cpt
icon Preview = "glyphicon-eye-open" icon Preview = "glyphicon-eye-open"
icon Both = "glyphicon-transfer" icon Both = "glyphicon-transfer"
icon Code = "glyphicon-pencil" icon Code = "glyphicon-pencil"
type Controls =
(
codeElRef :: R.Ref (Nullable Element)
, codeType :: R.State CodeType
, codeOverlayElRef :: R.Ref (Nullable Element)
, editorCodeRef :: R.Ref Code
, error :: R.State (Maybe Error)
, htmlElRef :: R.Ref (Nullable Element)
, viewType :: R.State ViewType
)
initControls :: Code -> CodeType -> R.Hooks (Record Controls)
initControls code defaultCodeType = do
htmlElRef <- R.useRef null
codeElRef <- R.useRef null
codeOverlayElRef <- R.useRef null
codeType <- R.useState' defaultCodeType
editorCodeRef <- R.useRef code
error <- R.useState' Nothing
viewType <- R.useState' Both
pure $ {
codeElRef
, codeType
, codeOverlayElRef
, editorCodeRef
, error
, htmlElRef
, viewType
}
'use strict';
const hljs = require('highlightjs/highlight.pack.min.js');
function highlightBlock(el) {
hljs.highlightBlock(el);
}
exports._highlightBlock = highlightBlock;
module Gargantext.Utils.HighlightJS where
import DOM.Simple (Element)
import Effect (Effect)
import Effect.Uncurried (EffectFn1, runEffectFn1)
import Gargantext.Prelude
highlightBlock :: Element -> Effect Unit
highlightBlock el = runEffectFn1 _highlightBlock el
foreign import _highlightBlock :: EffectFn1 Element Unit
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
<link href="styles/bootstrap.min.css" rel="stylesheet"> <link href="styles/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="styles/context-menu.css"/> <link rel="stylesheet" type="text/css" href="styles/context-menu.css"/>
<link rel="stylesheet" type="text/css" href="styles/menu.css"/> <link rel="stylesheet" type="text/css" href="styles/menu.css"/>
<link rel="stylesheet" type="text/css" href="styles/highlightjs-solarized-light.css"/>
<link href="styles/Graph.css" rel="stylesheet" type="text/css" /> <link href="styles/Graph.css" rel="stylesheet" type="text/css" />
<link href="styles/Login.css" rel="stylesheet" type="text/css" /> <link href="styles/Login.css" rel="stylesheet" type="text/css" />
<link href="styles/CodeEditor.css" rel="stylesheet" type="text/css" /> <link href="styles/CodeEditor.css" rel="stylesheet" type="text/css" />
......
...@@ -3261,6 +3261,11 @@ he@1.2.x, he@^1.1.1: ...@@ -3261,6 +3261,11 @@ he@1.2.x, he@^1.1.1:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
highlightjs@^9.16.2:
version "9.16.2"
resolved "https://registry.yarnpkg.com/highlightjs/-/highlightjs-9.16.2.tgz#07ea6cc7c93340fc440734fb7abf28558f1f0fe1"
integrity sha512-FK1vmMj8BbEipEy8DLIvp71t5UsC7n2D6En/UfM/91PCwmOpj6f2iu0Y0coRC62KSRHHC+dquM2xMULV/X7NFg==
hmac-drbg@^1.0.0: hmac-drbg@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
...@@ -5177,6 +5182,15 @@ purescript-installer@^0.2.0: ...@@ -5177,6 +5182,15 @@ purescript-installer@^0.2.0:
which "^1.3.1" which "^1.3.1"
zen-observable "^0.8.14" zen-observable "^0.8.14"
purescript-language-server@^0.12.7:
version "0.12.7"
resolved "https://registry.yarnpkg.com/purescript-language-server/-/purescript-language-server-0.12.7.tgz#0edc0536b46ad0f9cfd93d85c62c4bbb3d22a5e2"
integrity sha512-uXYrzoKPjlgEOgJqctqcfddtkCa57N3/1t2wENxS8TnxJnZt1j/LVQCWMhrEUfw90DPohhRKaaQmdrWrCpq9ow==
dependencies:
vscode-languageserver "^3.2.0"
vscode-uri "^1.0.0"
which "^1.2.9"
purescript@^0.13.6: purescript@^0.13.6:
version "0.13.6" version "0.13.6"
resolved "https://registry.yarnpkg.com/purescript/-/purescript-0.13.6.tgz#f5f77680dd7a50b7e63ba671bd85e2ac121318b0" resolved "https://registry.yarnpkg.com/purescript/-/purescript-0.13.6.tgz#f5f77680dd7a50b7e63ba671bd85e2ac121318b0"
...@@ -6614,6 +6628,37 @@ vm-browserify@^1.0.0, vm-browserify@^1.0.1: ...@@ -6614,6 +6628,37 @@ vm-browserify@^1.0.0, vm-browserify@^1.0.1:
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.0.tgz#bd76d6a23323e2ca8ffa12028dc04559c75f9019" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.0.tgz#bd76d6a23323e2ca8ffa12028dc04559c75f9019"
integrity sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw== integrity sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==
vscode-jsonrpc@3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-3.5.0.tgz#87239d9e166b2d7352245b8a813597804c1d63aa"
integrity sha1-hyOdnhZrLXNSJFuKgTWXgEwdY6o=
vscode-languageserver-protocol@3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.5.1.tgz#5144a3a9eeccbd83fe2745bd4ed75fad6cc45f0d"
integrity sha512-1fPDIwsAv1difCV+8daOrJEGunClNJWqnUHq/ncWrjhitKWXgGmRCjlwZ3gDUTt54yRcvXz1PXJDaRNvNH6pYA==
dependencies:
vscode-jsonrpc "3.5.0"
vscode-languageserver-types "3.5.0"
vscode-languageserver-types@3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.5.0.tgz#e48d79962f0b8e02de955e3f524908e2b19c0374"
integrity sha1-5I15li8LjgLelV4/UkkI4rGcA3Q=
vscode-languageserver@^3.2.0:
version "3.5.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-3.5.1.tgz#e0044b7df4d2447ce12632dfc98f1ab0afacbdff"
integrity sha512-RYUKn0DgHTFcS8kS4VaNCjNMaQXYqiXdN9bKrFjXzu5RPKfjIYcoh47oVWwZj4L3R/DPB0Se7HPaDatvYY2XgQ==
dependencies:
vscode-languageserver-protocol "3.5.1"
vscode-uri "^1.0.1"
vscode-uri@^1.0.0, vscode-uri@^1.0.1:
version "1.0.8"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.8.tgz#9769aaececae4026fb6e22359cb38946580ded59"
integrity sha512-obtSWTlbJ+a+TFRYGaUumtVwb+InIUVI0Lu0VBUAPmj2cU5JutEXg3xUE0c2J5Tcy7h2DEKVJBFi+Y9ZSFzzPQ==
wait-for-expect@^1.1.1: wait-for-expect@^1.1.1:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-1.2.0.tgz#fdab6a26e87d2039101db88bff3d8158e5c3e13f" resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-1.2.0.tgz#fdab6a26e87d2039101db88bff3d8158e5c3e13f"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment