├── .gitattributes ├── assets ├── demo.gif └── before-after.png ├── docs ├── logo19.png ├── logo38.png ├── logo128.png ├── logo19_gray.png ├── logo38_gray.png ├── oceanic-next.css ├── popup.html ├── codemirror.css └── ocamlDoc.css ├── src ├── images │ ├── logo.png │ ├── logo128.png │ ├── logo19.png │ ├── logo38.png │ ├── logo64.png │ ├── logo19_gray.png │ ├── logo38_gray.png │ ├── ocamlLogo128.png │ └── ocamlLogo.svg ├── extension │ ├── common │ │ ├── globals.re │ │ ├── vendor │ │ │ ├── copyToClipboard.re │ │ │ └── codeMirror.re │ │ ├── hljs.re │ │ ├── components │ │ │ ├── copyButton.re │ │ │ ├── editor.re │ │ │ ├── transition.re │ │ │ ├── copyConfirmation.re │ │ │ └── openButton.re │ │ ├── message.re │ │ ├── core.re │ │ ├── chrome.re │ │ ├── protocol.re │ │ └── localDom.re │ ├── contentLoader.re │ ├── popup │ │ ├── columnTitle.re │ │ ├── popupStyles.re │ │ ├── popupCommon.re │ │ └── popupWindow.re │ ├── content │ │ ├── inline │ │ │ ├── inlineError.re │ │ │ ├── inlinePopover.re │ │ │ ├── inlineListing.re │ │ │ └── inlineStyles.re │ │ ├── common.re │ │ ├── convert │ │ │ ├── replace.re │ │ │ ├── detect.re │ │ │ └── retrieve.re │ │ ├── convertPage.re │ │ └── overlay.re │ ├── content.re │ ├── page.re │ ├── popup.re │ └── background.re ├── refmt │ ├── RefmtShared.re │ └── refmt2.re ├── manifest.json ├── css │ ├── oceanic-next.css │ ├── codemirror.css │ └── ocamlDoc.css └── popup.html ├── .gitignore ├── circle.yml ├── .screenrc ├── bsconfig.json ├── webpack.production.config.js ├── LICENSE ├── deploy └── deploy.js ├── package.json ├── webpack.config.js └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | docs/Popup.bundle.js binary -------------------------------------------------------------------------------- /assets/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonml/reason-tools/HEAD/assets/demo.gif -------------------------------------------------------------------------------- /docs/logo19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonml/reason-tools/HEAD/docs/logo19.png -------------------------------------------------------------------------------- /docs/logo38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonml/reason-tools/HEAD/docs/logo38.png -------------------------------------------------------------------------------- /docs/logo128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonml/reason-tools/HEAD/docs/logo128.png -------------------------------------------------------------------------------- /docs/logo19_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonml/reason-tools/HEAD/docs/logo19_gray.png -------------------------------------------------------------------------------- /docs/logo38_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonml/reason-tools/HEAD/docs/logo38_gray.png -------------------------------------------------------------------------------- /src/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonml/reason-tools/HEAD/src/images/logo.png -------------------------------------------------------------------------------- /assets/before-after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonml/reason-tools/HEAD/assets/before-after.png -------------------------------------------------------------------------------- /src/images/logo128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonml/reason-tools/HEAD/src/images/logo128.png -------------------------------------------------------------------------------- /src/images/logo19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonml/reason-tools/HEAD/src/images/logo19.png -------------------------------------------------------------------------------- /src/images/logo38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonml/reason-tools/HEAD/src/images/logo38.png -------------------------------------------------------------------------------- /src/images/logo64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonml/reason-tools/HEAD/src/images/logo64.png -------------------------------------------------------------------------------- /src/images/logo19_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonml/reason-tools/HEAD/src/images/logo19_gray.png -------------------------------------------------------------------------------- /src/images/logo38_gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonml/reason-tools/HEAD/src/images/logo38_gray.png -------------------------------------------------------------------------------- /src/images/ocamlLogo128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reasonml/reason-tools/HEAD/src/images/ocamlLogo128.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _build/** 2 | lib 3 | 4 | node_modules 5 | *.log 6 | .merlin 7 | 8 | .DS_Store 9 | .bsb.lock 10 | -------------------------------------------------------------------------------- /src/extension/common/globals.re: -------------------------------------------------------------------------------- 1 | [@bs.val] external ocamlVersion : string = "__OCAML_VERSION__"; 2 | 3 | [@bs.val] external reasonVersion : string = "__REASON_VERSION__"; 4 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | node: 3 | version: 6.1.0 4 | compile: 5 | override: 6 | - npm run build:prod 7 | deployment: 8 | production: 9 | branch: master 10 | commands: 11 | - node deploy/deploy.js 12 | -------------------------------------------------------------------------------- /.screenrc: -------------------------------------------------------------------------------- 1 | sessionname "reason-tools build console" 2 | defscrollback 10000 3 | screen -t "bsb -- `C-a \` to quit" 0 ./node_modules/.bin/bsb -make-world -w 4 | split 5 | focus down 6 | screen -t "webpack -- `C-a \` to quit - `C-a Esc` to scroll" 1 npm run watch:js 7 | -------------------------------------------------------------------------------- /src/extension/contentLoader.re: -------------------------------------------------------------------------------- 1 | let loaded = ref(false); 2 | 3 | Protocol.Storage.queryDisabled( 4 | (disabled) => 5 | if (! disabled && Detect.shouldConvert()) { 6 | Protocol.LoadScripts.send() 7 | } 8 | ); 9 | 10 | Protocol.NotifyLoaded.listen(() => loaded := true); 11 | 12 | Protocol.QueryLoaded.listen(() => loaded^); 13 | -------------------------------------------------------------------------------- /src/extension/common/vendor/copyToClipboard.re: -------------------------------------------------------------------------------- 1 | [@bs.module] external copyToClipboard : ReasonReact.reactClass = "react-copy-to-clipboard"; 2 | 3 | let make = (~text: string, ~onCopy: unit => unit, children) => 4 | ReasonReact.wrapJsForReason( 5 | ~reactClass=copyToClipboard, 6 | ~props={"text": text, "onCopy": onCopy}, 7 | children 8 | ); 9 | -------------------------------------------------------------------------------- /src/extension/common/hljs.re: -------------------------------------------------------------------------------- 1 | [@bs.module "highlight.js/lib/highlight"] 2 | external registerLanguage : (string, string) => unit = "registerLanguage"; 3 | 4 | [@bs.module "highlight.js/lib/highlight"] external configure : Js.t({..}) => unit = "configure"; 5 | 6 | [@bs.module "highlight.js/lib/highlight"] 7 | external highlightBlock : LocalDom.Element.t => unit = "highlightBlock"; 8 | -------------------------------------------------------------------------------- /src/extension/common/components/copyButton.re: -------------------------------------------------------------------------------- 1 | let component = ReasonReact.statelessComponent("CopyButton"); 2 | 3 | let make = (~label="copy", ~text, ~onCopy, ~style=?, _) => { 4 | ...component, 5 | render: (_) => 6 | 7 | (ReasonReact.stringToElement(label)) 8 | 9 | }; 10 | -------------------------------------------------------------------------------- /bsconfig.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "name" : "reason-tools", 4 | "version": "0.0.0", 5 | "reason" : {"react-jsx" : 2}, 6 | "bs-dependencies": ["reason-react", "bs-result"], 7 | "sources": { 8 | "dir": "src", 9 | "subdirs": [ 10 | {"dir": "extension", "subdirs": true}, 11 | {"dir": "refmt"} 12 | ] 13 | }, 14 | "package-specs": ["es6"], 15 | "refmt": 3 16 | } 17 | -------------------------------------------------------------------------------- /src/extension/popup/columnTitle.re: -------------------------------------------------------------------------------- 1 | let showVersion = (lang) => 2 | switch lang { 3 | | RefmtShared.RE => Globals.reasonVersion 4 | | RefmtShared.ML => Globals.ocamlVersion 5 | | _ => "" 6 | }; 7 | 8 | let component = ReasonReact.statelessComponent("ColumnTItle"); 9 | 10 | let make = (~lang, ~select, _) => { 11 | ...component, 12 | render: (_) => select 13 | }; 14 | -------------------------------------------------------------------------------- /src/extension/common/components/editor.re: -------------------------------------------------------------------------------- 1 | let component = ReasonReact.statelessComponent("Editor"); 2 | 3 | let make = (~value, ~autoFocus=?, ~editorDidMount=?, ~lang, ~readOnly=false, ~onChange=?, _) => { 4 | ...component, 5 | render: (_) => 6 | 17 | }; 18 | -------------------------------------------------------------------------------- /src/extension/content/inline/inlineError.re: -------------------------------------------------------------------------------- 1 | let component = ReasonReact.statelessComponent("InlineError"); 2 | 3 | let make = (~message, ~close, _) => { 4 | ...component, 5 | render: (_) => 6 |
7 | 10 |
close())> 11 |
(ReasonReact.stringToElement(message))
12 |
13 |
14 |
15 | }; 16 | -------------------------------------------------------------------------------- /src/extension/common/components/transition.re: -------------------------------------------------------------------------------- 1 | type action = 2 | | Style(ReactDOMRe.Style.t); 3 | 4 | let component = ReasonReact.reducerComponent("Transition"); 5 | 6 | let make = (~before, ~after, children) => { 7 | ...component, 8 | reducer: (action, _state) => 9 | switch action { 10 | | Style(a) => ReasonReact.Update(a) 11 | }, 12 | initialState: () => before, 13 | didMount: (self) => { 14 | /*TODO: why carry the payload?*/ 15 | ignore(Js.Global.setTimeout(self.reduce((_) => Style(after)), 0)); 16 | ReasonReact.NoUpdate 17 | }, 18 | render: ({state}) =>
(ReasonReact.arrayToElement(children))
19 | }; 20 | -------------------------------------------------------------------------------- /src/extension/content.re: -------------------------------------------------------------------------------- 1 | open LocalDom; 2 | 3 | Hljs.registerLanguage("ocaml", [%bs.raw "require('highlight.js/lib/languages/ocaml')"]); 4 | 5 | Hljs.configure({"classPrefix": "", "languages": [|"ocaml"|]}); 6 | 7 | Protocol.Storage.queryDisabled( 8 | (disabled) => 9 | if (! disabled && Detect.shouldConvert()) { 10 | ConvertPage.toggle() 11 | } 12 | ); 13 | 14 | Protocol.ToggleConversion.listen(ConvertPage.toggle); 15 | 16 | Protocol.RefmtSelection.listen( 17 | () => { 18 | let selection = Window.getSelection(); 19 | let text = selection |> Selection.toString; 20 | Selection.removeAllRanges(selection); 21 | Overlay.try_(text) 22 | } 23 | ); 24 | -------------------------------------------------------------------------------- /src/extension/common/components/copyConfirmation.re: -------------------------------------------------------------------------------- 1 | let iconStyle = ReactDOMRe.Style.make(~fontSize="16vh", ()); 2 | 3 | let textStyle = ReactDOMRe.Style.make(~fontSize="3vh", ~whiteSpace="nowrap", ~marginTop="4vh", ()); 4 | 5 | let component = ReasonReact.statelessComponent("CopyConfirmation"); 6 | 7 | let make = (~show, ~text="Text copied to clipboard", ~style=?, _) => { 8 | ...component, 9 | render: (_) => 10 | show ? 11 |
12 |
(ReasonReact.stringToElement(Js.String.fromCodePoint(0x2398)))
13 |
(ReasonReact.stringToElement(text))
14 |
: 15 | ReasonReact.nullElement 16 | }; 17 | -------------------------------------------------------------------------------- /src/extension/common/components/openButton.re: -------------------------------------------------------------------------------- 1 | let component = ReasonReact.statelessComponent("OpenButton"); 2 | 3 | let make = (~onClick, ~style=?, _) => { 4 | ...component, 5 | render: (_) => 6 | 7 | 11 | /* Unsupported attribute */ 12 | 13 | 16 | 17 | 18 | }; 19 | -------------------------------------------------------------------------------- /src/extension/content/inline/inlinePopover.re: -------------------------------------------------------------------------------- 1 | let component = ReasonReact.statelessComponent("InlinePopover"); 2 | 3 | let make = (~inLang, ~inText, ~outLang, ~outText, ~close, ~open_, _) => { 4 | ...component, 5 | render: (_) => 6 |
7 | 10 |
close())> 11 | 12 | 13 |
14 |
15 |
16 | }; 17 | -------------------------------------------------------------------------------- /src/extension/common/message.re: -------------------------------------------------------------------------------- 1 | open Core; 2 | 3 | type message('a) = { 4 | type_: string, 5 | message: 'a 6 | }; 7 | 8 | let send = (type_, message) => Chrome.Runtime.sendMessage({type_, message}, noop); 9 | 10 | let sendTab = (id, type_, message) => Chrome.Tabs.sendMessage(id, {type_, message}, noop); 11 | 12 | let query = (type_, message, callback) => Chrome.Runtime.sendMessage({type_, message}, callback); 13 | 14 | let queryTab = (id, type_, message, callback) => 15 | Chrome.Tabs.sendMessage(id, {type_, message}, callback); 16 | 17 | let receive = (type_, callback) => 18 | Chrome.Runtime.addMessageListener( 19 | (request, sender, respond) => 20 | if (request.type_ == type_) { 21 | callback(request.message, sender, respond) 22 | } 23 | ); 24 | -------------------------------------------------------------------------------- /src/extension/page.re: -------------------------------------------------------------------------------- 1 | open LocalDom; 2 | 3 | let onOpen: string => unit = [%bs.raw 4 | {| 5 | function (hash) { 6 | window.open( 7 | window.location.href, 8 | "_blank" 9 | ); 10 | } 11 | |} 12 | ]; 13 | 14 | let refmt = 15 | ( 16 | input, 17 | ~inLang=RefmtShared.UnknownLang, 18 | ~inType=RefmtShared.UnknownType, 19 | ~outLang=RefmtShared.UnknownLang, 20 | cb 21 | ) => { 22 | Protocol.Refmt.send( 23 | input, 24 | ~inLang, 25 | ~inType, 26 | ~outLang, 27 | (error) => 28 | switch error { 29 | | Result.Error(error) => cb(error, RefmtShared.UnknownLang, RefmtShared.UnknownLang) 30 | | Result.Ok({outText, inLang, outLang}) => cb(outText, inLang, outLang) 31 | } 32 | ); 33 | Protocol.Storage.setLatestInput(input) 34 | }; 35 | 36 | Document.addEventListener("DOMContentLoaded", () => PopupCommon.init(~onOpen, ~refmt, ())); 37 | -------------------------------------------------------------------------------- /webpack.production.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const config = require('./webpack.config'); 4 | const ZipPlugin = require('zip-webpack-plugin'); 5 | 6 | 7 | const prodConfig = config.map((config) => { 8 | if (!config.plugins) { 9 | config.plugins = []; 10 | } 11 | config.plugins = config.plugins.concat([ 12 | new webpack.LoaderOptionsPlugin({ 13 | minimize: true 14 | }), 15 | new webpack.DefinePlugin({ 16 | 'process.env':{ 17 | 'NODE_ENV': JSON.stringify('production') 18 | } 19 | }), 20 | new webpack.optimize.UglifyJsPlugin(), 21 | new webpack.optimize.AggressiveMergingPlugin(), 22 | ]); 23 | if (config.name === "Extension") { 24 | config.plugins = config.plugins.concat([ 25 | new ZipPlugin({ 26 | filename: 'reason-tools.zip', 27 | }) 28 | ]); 29 | } 30 | return config; 31 | }); 32 | 33 | 34 | module.exports = prodConfig; 35 | -------------------------------------------------------------------------------- /src/refmt/RefmtShared.re: -------------------------------------------------------------------------------- 1 | type ast; 2 | 3 | type result('thing) = 4 | | REI('thing) 5 | | RE('thing) 6 | | ML('thing) 7 | | MLI('thing) 8 | | REO('thing) 9 | | REOI('thing); 10 | 11 | type parseResult = 12 | | Ast(result(ast)) 13 | | Error(result(Js.Exn.t)); 14 | 15 | type language = 16 | | ML 17 | | RE 18 | | REO 19 | | UnknownLang; 20 | 21 | let languageOfString = (str) => 22 | switch str { 23 | | "ML" => ML 24 | | "RE" => RE 25 | | "REO" => REO 26 | | _ => UnknownLang 27 | }; 28 | 29 | let stringOfLanguage = (lang) => 30 | switch lang { 31 | | ML => "ML" 32 | | RE => "RE" 33 | | REO => "REO" 34 | | UnknownLang => "Unkown" 35 | }; 36 | 37 | type codeType = 38 | | Implementation 39 | | Interface 40 | | UnknownType; 41 | 42 | let typeOfString = (str) => 43 | switch str { 44 | | "implementation" => Implementation 45 | | "interface" => Interface 46 | | _ => UnknownType 47 | }; 48 | 49 | let stringOfType = (codeType) => 50 | switch codeType { 51 | | Implementation => "implementation" 52 | | Interface => "interface" 53 | | UnknownType => "Unkown" 54 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Ricky Vetter 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/extension/common/vendor/codeMirror.re: -------------------------------------------------------------------------------- 1 | type editor; 2 | 3 | [@bs.module "react-codemirror2"] external codeMirror : ReasonReact.reactClass = "Controlled"; 4 | [@bs.send] external execCommand : (editor, string) => unit = ""; 5 | 6 | let make = 7 | ( 8 | ~autoFocus: option(bool)=?, 9 | ~style: option(Js.t({..}))=?, 10 | ~value: option(string)=?, 11 | ~editorDidMount: option(editor => unit)=?, 12 | ~onChange: option(string => unit)=?, 13 | ~options: option(Js.t({..}))=?, 14 | children 15 | ) => 16 | ReasonReact.wrapJsForReason( 17 | ~reactClass=codeMirror, 18 | ~props={ 19 | "autoFocus": switch (autoFocus) { 20 | | Some(true) => Js.true_ 21 | | Some(false) | None => Js.false_ 22 | }, 23 | "style": Js.Undefined.fromOption(style), 24 | "value": Js.Undefined.fromOption(value), 25 | "onBeforeChange": (_editor, _data, value) => switch (onChange) { 26 | | Some(onChange) => onChange(value) 27 | | None => () 28 | }, 29 | "editorDidMount": Js.Undefined.fromOption(editorDidMount), 30 | "options": Js.Undefined.fromOption(options) 31 | }, 32 | children 33 | ); 34 | -------------------------------------------------------------------------------- /deploy/deploy.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const ChromeWebstoreApi = require('chrome-store-api').Webstore; 4 | const ChromeTokenManager = require('chrome-store-api').TokenManager; 5 | const ChromeFileStorage = require('chrome-store-api').FileStorage; 6 | 7 | const reasonToolsZip = path.join( 8 | __dirname, 9 | '../_build/extension/reason-tools.zip' 10 | ); 11 | 12 | function deployChrome(toTrustedTesters) { 13 | const chromeCode = process.env.CHROME_CODE; 14 | const chromeClientId = process.env.CHROME_CLIENT_ID; 15 | const chromeClientSecret = process.env.CHROME_CLIENT_SECRET; 16 | 17 | const storage = new ChromeFileStorage(path.join( 18 | process.env.CIRCLE_ARTIFACTS, 19 | 'chromeTokenStorage.json' 20 | )); 21 | const tokenManager = new ChromeTokenManager( 22 | chromeCode, 23 | chromeClientId, 24 | chromeClientSecret, 25 | storage 26 | ); 27 | const api = new ChromeWebstoreApi(tokenManager); 28 | 29 | api 30 | .update('kmdelnjbembbiodplmhgfjpecibfhadd', fs.readFileSync(reasonToolsZip)) 31 | .then((data) => api.publish(data.id)) 32 | .then((data) => console.log(data)) 33 | .catch((err) => console.log(err)); 34 | }; 35 | 36 | deployChrome(); 37 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Reason Tools", 4 | "description": "Adds Reason to the browser", 5 | "icons": { 6 | "19": "logo19.png", 7 | "38": "logo38.png", 8 | "128": "logo128.png" 9 | }, 10 | "background": { 11 | "scripts": ["Background.bundle.js"] 12 | }, 13 | "browser_action": { 14 | "default_icon": { 15 | "19": "logo19.png", 16 | "38": "logo38.png" 17 | }, 18 | "default_popup": "popup.html", 19 | "default_title": "Reason Tools" 20 | }, 21 | "content_scripts": [ 22 | { 23 | "matches": [""], 24 | "js": ["ContentLoader.bundle.js"], 25 | "run_at": "document_end", 26 | "all_frames": true 27 | } 28 | ], 29 | "commands": { 30 | "toggle_between_interface_and_implementation": { 31 | "suggested_key": { 32 | "default": "Alt+I" 33 | }, 34 | "description": "Toggle between interface and implementation files" 35 | }, 36 | "_execute_browser_action": { 37 | "suggested_key": { 38 | "default": "Alt+D" 39 | } 40 | } 41 | }, 42 | "permissions": [ 43 | "activeTab", 44 | "storage", 45 | "contextMenus", 46 | "http://*/", 47 | "https://*/" 48 | ], 49 | "web_accessible_resources": [ 50 | "ocamlDoc.css", 51 | "logo128.png", 52 | "ocamlLogo128.png" 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reason-tools", 3 | "version": "0.3.2", 4 | "description": "Adds Reason to the browser", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/reasonml/reason-tools.git" 8 | }, 9 | "author": "rickyvetter", 10 | "license": "MIT", 11 | "homepage": "https://github.com/reasonml/reason-tools", 12 | "keywords": [ 13 | "reason", 14 | "reasonml", 15 | "ocaml", 16 | "extension", 17 | "browser", 18 | "chrome" 19 | ], 20 | "scripts": { 21 | "build:self": "bsb -make-world", 22 | "clean": "bsb -clean-world", 23 | "build:js": "webpack", 24 | "build": "npm run build:self && npm run build:js", 25 | "build:prod": "npm run build:self && webpack -p --config ./webpack.production.config.js", 26 | "watch:js": "webpack -w", 27 | "watch:screen": "screen -c .screenrc", 28 | "test": "exit 0" 29 | }, 30 | "dependencies": { 31 | "codemirror": "^5.34.0", 32 | "highlight.js": "^9.8.0", 33 | "react": "^16.0.0", 34 | "react-codemirror2": "^4.0.0", 35 | "react-copy-to-clipboard": "^5.0.1", 36 | "react-dom": "^16.0.0", 37 | "reason-react": "^0.3.0" 38 | }, 39 | "devDependencies": { 40 | "bs-platform": "^2.2.1", 41 | "bs-result": "^1.1.0", 42 | "chrome-store-api": "^1.0.5", 43 | "file-loader": "^1.1.5", 44 | "generate-json-webpack-plugin": "^0.2.1", 45 | "webpack": "^2.4.1", 46 | "zip-webpack-plugin": "^2.0.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/extension/content/common.re: -------------------------------------------------------------------------------- 1 | open LocalDom; 2 | 3 | let normalizeText = (text) => 4 | text 5 | |> Js.String.trim 6 | |> Js.String.replaceByRe([%bs.re {|/[^\x00-\x7F]/g|}], " ") 7 | |> Js.String.replace(Js.String.fromCharCode(65533), ""); 8 | 9 | let untoplevel = (text) => 10 | Js.String.( 11 | if (text |> startsWith("# ")) { 12 | text |> sliceToEnd(~from=2) 13 | } else { 14 | text 15 | } 16 | ); 17 | 18 | let getElementsByTagName = (maybeEl, name) => 19 | ( 20 | switch maybeEl { 21 | | Some(el) => Element.getElementsByTagName(el, name) 22 | | None => Document.getElementsByTagName(name) 23 | } 24 | ) 25 | |> Arrayish.toArray 26 | |> Array.to_list; 27 | 28 | let getElementsByClassName = (maybeEl, name) => 29 | ( 30 | switch maybeEl { 31 | | Some(el) => Element.getElementsByClassName(el, name) 32 | | None => Document.getElementsByClassName(name) 33 | } 34 | ) 35 | |> Arrayish.toArray 36 | |> Array.to_list; 37 | 38 | let querySelectorAll = (el, selector) => 39 | Element.querySelectorAll(el, selector) |> Arrayish.toArray |> Array.to_list; 40 | 41 | let createStylesheet = () => { 42 | let stylesheet = Document.createElement("link"); 43 | let css = [%bs.raw {|require('../../../../../src/css/ocamlDoc.css')|}]; 44 | Element.setType(stylesheet, "text/css"); 45 | Element.setRel(stylesheet, "stylesheet"); 46 | Element.setHref(stylesheet, Chrome.Extension.getURL(css)); 47 | stylesheet 48 | }; 49 | -------------------------------------------------------------------------------- /docs/oceanic-next.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Name: Base16 Oceanic Next Dark 4 | Author: Dmitri Voronianski (http://pixelhunter.me) 5 | 6 | CodeMirror template by Jan T. Sott (https://github.com/idleberg) 7 | Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) 8 | 9 | */ 10 | 11 | .cm-s-oceanic-next.CodeMirror {background: #1B2B34; color: #CDD3DE;} 12 | .cm-s-oceanic-next div.CodeMirror-selected {background: #343D46 !important;} 13 | .cm-s-oceanic-next .CodeMirror-gutters {background: #1B2B34; border-right: 0px;} 14 | .cm-s-oceanic-next .CodeMirror-linenumber {color: #65737E;} 15 | .cm-s-oceanic-next .CodeMirror-cursor {border-left: 1px solid #A7ADBA !important;} 16 | 17 | .cm-s-oceanic-next span.cm-comment {color: #AB7967;} 18 | .cm-s-oceanic-next span.cm-atom {color: #C594C5;} 19 | .cm-s-oceanic-next span.cm-number {color: #C594C5;} 20 | 21 | .cm-s-oceanic-next span.cm-property, .cm-s-oceanic-next span.cm-attribute {color: #99C794;} 22 | .cm-s-oceanic-next span.cm-keyword {color: #EC5f67;} 23 | .cm-s-oceanic-next span.cm-string {color: #FAC863;} 24 | 25 | .cm-s-oceanic-next span.cm-variable {color: #CDD3DE;} 26 | .cm-s-oceanic-next span.cm-variable-2 {color: #6699CC;} 27 | .cm-s-oceanic-next span.cm-def {color: #F99157;} 28 | .cm-s-oceanic-next span.cm-error {background: #EC5f67; color: #A7ADBA;} 29 | .cm-s-oceanic-next span.cm-bracket {color: #CDD3DE;} 30 | .cm-s-oceanic-next span.cm-tag {color: #EC5f67;} 31 | .cm-s-oceanic-next span.cm-link {color: #C594C5;} 32 | 33 | .cm-s-oceanic-next .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;} 34 | -------------------------------------------------------------------------------- /src/css/oceanic-next.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Name: Base16 Oceanic Next Dark 4 | Author: Dmitri Voronianski (http://pixelhunter.me) 5 | 6 | CodeMirror template by Jan T. Sott (https://github.com/idleberg) 7 | Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) 8 | 9 | */ 10 | 11 | .cm-s-oceanic-next.CodeMirror {background: #1B2B34; color: #CDD3DE;} 12 | .cm-s-oceanic-next div.CodeMirror-selected {background: #343D46 !important;} 13 | .cm-s-oceanic-next .CodeMirror-gutters {background: #1B2B34; border-right: 0px;} 14 | .cm-s-oceanic-next .CodeMirror-linenumber {color: #65737E;} 15 | .cm-s-oceanic-next .CodeMirror-cursor {border-left: 1px solid #A7ADBA !important;} 16 | 17 | .cm-s-oceanic-next span.cm-comment {color: #AB7967;} 18 | .cm-s-oceanic-next span.cm-atom {color: #C594C5;} 19 | .cm-s-oceanic-next span.cm-number {color: #C594C5;} 20 | 21 | .cm-s-oceanic-next span.cm-property, .cm-s-oceanic-next span.cm-attribute {color: #99C794;} 22 | .cm-s-oceanic-next span.cm-keyword {color: #EC5f67;} 23 | .cm-s-oceanic-next span.cm-string {color: #FAC863;} 24 | 25 | .cm-s-oceanic-next span.cm-variable {color: #CDD3DE;} 26 | .cm-s-oceanic-next span.cm-variable-2 {color: #6699CC;} 27 | .cm-s-oceanic-next span.cm-def {color: #F99157;} 28 | .cm-s-oceanic-next span.cm-error {background: #EC5f67; color: #A7ADBA;} 29 | .cm-s-oceanic-next span.cm-bracket {color: #CDD3DE;} 30 | .cm-s-oceanic-next span.cm-tag {color: #EC5f67;} 31 | .cm-s-oceanic-next span.cm-link {color: #C594C5;} 32 | 33 | .cm-s-oceanic-next .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;} 34 | -------------------------------------------------------------------------------- /src/extension/content/convert/replace.re: -------------------------------------------------------------------------------- 1 | open LocalDom; 2 | 3 | open Common; 4 | 5 | let escapeRe = Js.String.replaceByRe([%bs.re {|/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g|}], "\\$&"); 6 | 7 | let replace = (this) => 8 | Js.String.replaceByRe(Js.Re.fromString("/\\b" ++ escapeRe(this) ++ "\\b/g")); 9 | 10 | let replaceAll = List.fold_left((out, (this, that)) => out |> replace(this, that)); 11 | 12 | let getNormalizedLinks = (els) => 13 | els 14 | |> List.map((el) => getElementsByTagName(Some(el), "a")) 15 | |> List.flatten 16 | |> List.map((a) => (Element.innerText(a), Element.href(a))); 17 | 18 | let getNormalizedIds = (els) => 19 | els 20 | |> List.map((el) => querySelectorAll(el, "[id]")) 21 | |> List.flatten 22 | |> List.map( 23 | (span) => { 24 | let text = normalizeText(Element.innerText(span)); 25 | let words = text |> Js.String.split(" "); 26 | let lastWord = words[Array.length(words) - 1]; 27 | (lastWord, Element.id(span)) 28 | } 29 | ); 30 | 31 | let replaceHrefs = (hrefs, out) => 32 | hrefs 33 | |> List.map(((text, href)) => (text, "" ++ text ++ "")) 34 | |> replaceAll(out); 35 | 36 | let replaceIds = (ids, out) => 37 | ids 38 | |> List.map( 39 | ((text, id)) => ( 40 | text, 41 | "" ++ text ++ "" 42 | ) 43 | ) 44 | |> replaceAll(out); 45 | 46 | let replaceListing = (els, text, replace) => { 47 | let hrefs = getNormalizedLinks(els); 48 | let ids = getNormalizedIds(els); 49 | let el = text |> replaceHrefs(hrefs) |> replaceIds(ids) |> replace; 50 | Hljs.highlightBlock(el) 51 | }; 52 | -------------------------------------------------------------------------------- /src/extension/popup.re: -------------------------------------------------------------------------------- 1 | open Core; 2 | 3 | open LocalDom; 4 | 5 | let mapOrElse = (resolve, reject, item) => 6 | switch (item) { 7 | | Some(res) => resolve(res) 8 | | None => reject() 9 | }; 10 | 11 | let getSelection = () => 12 | Promise.make( 13 | (resolve, reject) => 14 | Chrome.Tabs.executeScript( 15 | {"code": "window.getSelection().toString()"}, 16 | (maybeMaybeArray) => { 17 | maybeMaybeArray 18 | |> Js.Null_undefined.to_opt 19 | |> Js.Option.andThen([@bs](maybeArray) => Js.Array.findi((_, index) => index === 0, maybeArray)) 20 | |> Js.Option.andThen([@bs](s) => s == "" ? None : Some(s)) 21 | |> mapOrElse(resolve, reject); 22 | } 23 | ) 24 | ); 25 | 26 | let getLatestInput = () => 27 | Promise.make( 28 | (resolve, reject) => 29 | Protocol.Storage.queryLatestInput( 30 | (maybeInput) => maybeInput |> mapOrElse(resolve, reject) 31 | ) 32 | ); 33 | 34 | let refmt = 35 | ( 36 | input, 37 | ~inLang=RefmtShared.UnknownLang, 38 | ~inType=RefmtShared.UnknownType, 39 | ~outLang=RefmtShared.UnknownLang, 40 | cb 41 | ) => { 42 | Protocol.Refmt.send( 43 | input, 44 | ~inLang, 45 | ~inType, 46 | ~outLang, 47 | (error) => 48 | switch error { 49 | | Result.Error(error) => cb(error, RefmtShared.UnknownLang, RefmtShared.UnknownLang) 50 | | Result.Ok({outText, inLang, outLang}) => cb(outText, inLang, outLang) 51 | } 52 | ); 53 | Protocol.Storage.setLatestInput(input) 54 | }; 55 | 56 | let onOpen = Protocol.OpenInTab.send; 57 | 58 | Document.addEventListener( 59 | "DOMContentLoaded", 60 | PopupCommon.init(~getSelection, ~getLatestInput, ~onOpen, ~refmt) 61 | ); 62 | -------------------------------------------------------------------------------- /src/extension/common/core.re: -------------------------------------------------------------------------------- 1 | exception Unreachable; 2 | 3 | let noop = (_) => (); 4 | 5 | external id : 'a => 'a = "%identity"; 6 | 7 | module MaybeArray = { 8 | type t('a); 9 | /* 10 | let unwrap : t 'a => 'a = [%bs.raw {| 11 | function (maybeArray) { 12 | return Array.isArray(maybeArray) 13 | ? maybeArray[0] 14 | : maybeArray; 15 | } 16 | |}];*/ 17 | }; 18 | 19 | module Promise = { 20 | type t('a); 21 | [@bs.new] external make : (('a => unit, 'e => unit) => unit) => t('a) = "Promise"; 22 | [@bs.send.pipe : t('a)] external then_ : ('a => 'b) => t('b) = "then"; 23 | [@bs.send.pipe : t('a)] external and_then : ('a => t('b)) => t('b) = "then"; 24 | [@bs.send.pipe : t('a)] external catch : ('e => unit) => t('a) = ""; 25 | [@bs.send.pipe : t('a)] external or_ : ('e => 'b) => t('b) = "catch"; /* non-standard name for "overload" */ 26 | [@bs.send.pipe : t('a)] external or_else : ('e => t('b)) => t('b) = 27 | "catch"; /* non-standard name for "overload" */ 28 | [@bs.val] external all : array(t('a)) => t(array('a)) = "Promise.all"; 29 | [@bs.val] external race : array(t('a)) => t('b) = "Promise.race"; /* unsure about what the returned promise will hold */ 30 | [@bs.val] external reject : 'e => t('a) = "Promise.reject"; 31 | [@bs.val] external resolve : 'a => t('a) = "Promise.resolve"; 32 | }; 33 | 34 | module History = { 35 | [@bs.val] 36 | external replaceState : (~state: string, ~title: string, ~url: string) => unit = 37 | "window.history.replaceState"; 38 | }; 39 | 40 | module Util = { 41 | [@bs.val] external btoa : string => string = "window.btoa"; 42 | [@bs.val] external atob : string => string = "window.atob"; 43 | let classNames = (items) => 44 | items 45 | |> List.map(((name, flag)) => flag ? name : "") 46 | |> List.filter((s) => s !== "") 47 | |> String.concat(" "); 48 | }; 49 | -------------------------------------------------------------------------------- /src/extension/popup/popupStyles.re: -------------------------------------------------------------------------------- 1 | let popup = 2 | ReactDOMRe.Style.make( 3 | ~display="flex", 4 | ~flex="1", 5 | ~flexDirection="row", 6 | ~justifyContent="space-around", 7 | ~overflow="hidden", 8 | () 9 | ); 10 | 11 | let popupColumn = 12 | ReactDOMRe.Style.make( 13 | ~display="flex", 14 | ~flexDirection="column", 15 | ~paddingBottom="1%", 16 | ~position="relative", 17 | () 18 | ); 19 | 20 | let popupContext = 21 | ReactDOMRe.Style.make( 22 | ~alignItems="center", 23 | ~display="flex", 24 | ~flex="1", 25 | ~fontFamily="sans-serif", 26 | ~fontSize="3vh", 27 | ~justifyContent="space-between", 28 | ~maxHeight="25px", 29 | ~minHeight="16px", 30 | ~paddingBottom="0.5vh", 31 | ~paddingTop="0.5vh", 32 | ~width="100%", 33 | () 34 | ); 35 | 36 | let copyConfirmation = 37 | ReactDOMRe.Style.make( 38 | ~position="absolute", 39 | ~zIndex="10", 40 | ~top="50%", 41 | ~left="50%", 42 | ~transform="translate(-50%,-50%)", 43 | ~padding="4vh 8vh 6vh", 44 | ~display="flex", 45 | ~flexDirection="column", 46 | ~justifyContent="center", 47 | ~alignItems="center", 48 | ~borderRadius="2vh", 49 | ~backgroundColor="rgba(0,0,0,0.8)", 50 | ~color="white", 51 | ~fontFamily="sans-serif", 52 | /*pointerEvents::"none"*/ 53 | () 54 | ); 55 | 56 | let contextLink = 57 | ReactDOMRe.Style.make( 58 | ~textDecoration="none", 59 | ~color="rgb(219, 76, 63)", 60 | ~cursor="pointer", 61 | ~fontSize="16px", 62 | ~lineHeight="16px", 63 | ~paddingLeft="1vw", 64 | () 65 | ); 66 | 67 | let contextIcon = 68 | ReactDOMRe.Style.make( 69 | ~paddingLeft="1vw", 70 | ~cursor="pointer", /*fill::"rgb(219, 76, 63)"*/ 71 | ~height="16px", 72 | ~width="16px", 73 | () 74 | ); 75 | 76 | let contextTitle = ReactDOMRe.Style.make(~flex="1", ()); 77 | -------------------------------------------------------------------------------- /src/extension/content/convertPage.re: -------------------------------------------------------------------------------- 1 | open LocalDom; 2 | 3 | open Common; 4 | 5 | type saveState = {mutable stylesheets: list(Element.t)}; 6 | 7 | let savedState = {stylesheets: [createStylesheet()]}; 8 | 9 | let swapStyleSheets = (_) => { 10 | let stylesheets = 11 | getElementsByTagName(None, "link") 12 | |> List.filter((link) => Element.getAttribute(link, "rel") == "stylesheet"); 13 | stylesheets |> List.iter(Element.remove); 14 | savedState.stylesheets |> List.iter(Head.appendChild); 15 | savedState.stylesheets = stylesheets 16 | }; 17 | 18 | let readjustViewport = () => 19 | if (! (Location.hash == "")) { 20 | [%bs.raw {| window.location.href = window.location.href |}] 21 | }; 22 | 23 | type swapState = {mutable remaining: int}; 24 | 25 | let doListing = (mode, state, listing) => { 26 | open Retrieve; 27 | let {els, text, replace} = listing; 28 | Protocol.Refmt.send( 29 | text, 30 | (response) => { 31 | switch response { 32 | | Result.Error(_) => () /* TODO */ 33 | | Result.Ok({outText}) => Replace.replaceListing(els, outText, replace) 34 | }; 35 | /* we're in an async callback, so keep track of when we're finished by keeping count */ 36 | state.remaining = state.remaining - 1; 37 | if (mode == `initial && state.remaining <= 0) { 38 | /* when we're done, the DOM has most likely been shifted, 39 | * so we need to go back to where we're supposed to be */ 40 | readjustViewport 41 | () 42 | } 43 | } 44 | ) 45 | }; 46 | 47 | let swapSyntax = (mode) => { 48 | let listings = Retrieve.getListings(); 49 | let state = {remaining: List.length(listings)}; 50 | listings |> List.iter(doListing(mode, state)); 51 | if (mode != `initial) { 52 | Overlay.updateSyntaxSwapButton() 53 | } 54 | }; 55 | 56 | let toggle = () => { 57 | swapStyleSheets(); 58 | Overlay.toggle(swapStyleSheets, swapSyntax); 59 | swapSyntax(`initial) 60 | }; 61 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const GenerateJsonPlugin = require('generate-json-webpack-plugin'); 4 | const manifest = require('./src/manifest.json'); 5 | const package = require('./package.json'); 6 | 7 | manifest.version = package.version; 8 | 9 | const commonModule = { 10 | loaders: [ 11 | { 12 | test: /\.(png|jpg|gif|html|css)$/, 13 | loader: 'file-loader?name=[name].[ext]' 14 | } 15 | ], 16 | }; 17 | 18 | const Extension = { 19 | name: "Extension", 20 | entry: { 21 | Content: './lib/es6/src/extension/content.js', 22 | ContentLoader: './lib/es6/src/extension/contentLoader.js', 23 | Popup: './lib/es6/src/extension/popup.js', 24 | Background: './lib/es6/src/extension/background.js', 25 | }, 26 | output: { 27 | path: path.join(__dirname, '_build/extension/'), 28 | filename: '[name].bundle.js', 29 | }, 30 | module: commonModule, 31 | plugins: [ 32 | new GenerateJsonPlugin('manifest.json', manifest), 33 | new webpack.DefinePlugin({ 34 | '__REASON_VERSION__': '"3.0.0"', 35 | '__OCAML_VERSION__': '"4.02.3"' 36 | }), 37 | ], 38 | node: { 39 | fs: "empty" 40 | }, 41 | resolve: { 42 | alias: { 43 | react: path.resolve('./node_modules/react'), 44 | }, 45 | }, 46 | }; 47 | 48 | const Page = { 49 | name: "Page", 50 | entry: { 51 | Popup: './lib/es6/src/extension/page.js', 52 | }, 53 | output: { 54 | path: path.join(__dirname, 'docs/'), 55 | filename: '[name].bundle.js', 56 | }, 57 | plugins: [ 58 | new webpack.DefinePlugin({ 59 | '__REASON_VERSION__': '"3.0.0"', 60 | '__OCAML_VERSION__': '"4.02.3"' 61 | }), 62 | ], 63 | module: commonModule, 64 | node: { 65 | fs: "empty" 66 | }, 67 | resolve: { 68 | alias: { 69 | react: path.resolve('./node_modules/react'), 70 | }, 71 | }, 72 | }; 73 | 74 | module.exports = [Extension, Page]; 75 | -------------------------------------------------------------------------------- /src/extension/content/inline/inlineListing.re: -------------------------------------------------------------------------------- 1 | open Core; 2 | 3 | external refToElement : Dom.element => LocalDom.Element.t = "%identity"; 4 | 5 | type state = {preRef: ref(option(Dom.element))}; 6 | 7 | let updatePreRef = (r, {ReasonReact.state}) => state.preRef := Js.Nullable.to_opt(r); 8 | 9 | let component = ReasonReact.reducerComponent("InlineListing"); 10 | 11 | let make = (~lang, ~text, ~slideInFrom, ~open_, _) => { 12 | ...component, 13 | initialState: () => {preRef: ref(None)}, 14 | didMount: ({state}) => { 15 | switch state.preRef^ { 16 | | Some(r) => Hljs.highlightBlock(refToElement(r)) 17 | | None => () 18 | }; 19 | ReasonReact.NoUpdate 20 | }, 21 | reducer: ((), _state) => ReasonReact.NoUpdate, 22 | render: (self) => { 23 | let translateY = slideInFrom == "above" ? "-10vh" : "10vh"; 24 | let className = 25 | Util.classNames([ 26 | ("listing-container", true), 27 | ("ml", lang == RefmtShared.ML), 28 | ("re", lang == RefmtShared.RE) 29 | ]); 30 | 33 |
ReactEventRe.Mouse.stopPropagation(e))> 34 |
35 | (ReasonReact.stringToElement(Protocol.stringOfLanguage(lang))) 36 |
37 |
38 |
 (ReasonReact.stringToElement(text)) 
39 |
40 | 41 | open_(text)) 52 | /> 53 |
54 |
55 |
56 |
57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /src/extension/popup/popupCommon.re: -------------------------------------------------------------------------------- 1 | open Core; 2 | 3 | [%bs.raw {|require('../../../../../src/popup.html')|}]; 4 | 5 | [%bs.raw {|require('../../../../../src/images/logo19.png')|}]; 6 | 7 | [%bs.raw {|require('../../../../../src/images/logo38.png')|}]; 8 | 9 | [%bs.raw {|require('../../../../../src/images/logo19_gray.png')|}]; 10 | 11 | [%bs.raw {|require('../../../../../src/images/logo38_gray.png')|}]; 12 | 13 | [%bs.raw {|require('../../../../../src/images/logo128.png')|}]; 14 | 15 | [%bs.raw {|require('../../../../../src/css/codemirror.css')|}]; 16 | 17 | [%bs.raw {|require('../../../../../src/css/oceanic-next.css')|}]; 18 | 19 | [%bs.raw {|require('codemirror/mode/javascript/javascript')|}]; 20 | 21 | [%bs.raw {|require('codemirror/mode/mllike/mllike')|}]; 22 | 23 | let rejectedPromise = () => Promise.make((_, reject) => reject()); 24 | 25 | let setHash = (hash) => Core.History.replaceState(~state=[%bs.raw "{}"], ~title="", ~url=hash); 26 | 27 | let makeContentHash = (text) => "#" ++ Util.btoa(text); 28 | 29 | let generateShareableLink = (text) => "https://reasonml.github.io/reason-tools/popup.html" ++ text; 30 | 31 | let getInputFromUrl = () => { 32 | let text = LocalDom.Location.hash |> Js.String.sliceToEnd(~from=1) |> Util.atob; 33 | if (text == "") { 34 | Promise.reject() 35 | } else { 36 | Promise.resolve(text) 37 | } 38 | }; 39 | 40 | let init = 41 | ( 42 | ~getSelection=rejectedPromise, 43 | ~getLatestInput=rejectedPromise, 44 | ~onOpen, 45 | ~refmt: 46 | ( 47 | Js.String.t, 48 | ~inLang: Protocol.language=?, 49 | ~inType: Protocol.codeType=?, 50 | ~outLang: Protocol.language=?, 51 | (string, Protocol.language, Protocol.language) => 'a 52 | ) => 53 | unit, 54 | () 55 | ) => { 56 | let rec inputChanged = (~inLang=RefmtShared.UnknownLang, ~outLang=RefmtShared.UnknownLang, input) => { 57 | let hash = makeContentHash(input); 58 | let link = generateShareableLink(hash); 59 | setHash(hash); 60 | refmt( 61 | input, 62 | ~inLang, 63 | ~outLang, 64 | (outText, inLang, outLang) => render(input, outText, inLang, outLang, link) 65 | ) 66 | } 67 | and render = (inText, outText, inLang, outLang, link) => 68 | ReactDOMRe.renderToElementWithId( 69 | , 70 | "app" 71 | ); 72 | Promise.( 73 | getInputFromUrl() 74 | |> or_else(getSelection) 75 | |> or_else(getLatestInput) 76 | |> or_((_) => "") 77 | |> then_(inputChanged) 78 | |> ignore 79 | ) 80 | }; 81 | -------------------------------------------------------------------------------- /src/extension/content/inline/inlineStyles.re: -------------------------------------------------------------------------------- 1 | let stylesheet = {| 2 | .root-container { 3 | position: fixed; 4 | top: 0; 5 | bottom: 0; 6 | left: 0; 7 | right: 0; 8 | z-index: 65535; 9 | } 10 | 11 | .mask-container { 12 | position: absolute; 13 | box-sizing: border-box; 14 | padding: 5vw; 15 | top: 0; 16 | bottom: 0; 17 | left: 0; 18 | right: 0; 19 | display: flex; 20 | flex-direction: column; 21 | justify-content: center; 22 | background: rgba(0, 0, 0, 0.5); 23 | transition: background 4000ms; 24 | } 25 | 26 | .listing-container, 27 | .error-message-container { 28 | display: flex; 29 | 30 | box-shadow: 5px 5px 7px 0 rgba(0, 0, 0, 0.3); 31 | 32 | font-family: 'Helvetica', 'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif; 33 | font-size: 14px; 34 | line-height: 20px; 35 | background-color: #2b303b; 36 | color: #c0c5ce; 37 | 38 | font-weight: normal; /* external styles leaking in, why? */ 39 | 40 | margin: 0 auto; 41 | margin-bottom: 2vh; 42 | max-width: 1200px; 43 | } 44 | 45 | .error-message-container { 46 | padding: 1em 2em; 47 | color: #dd4b39; 48 | } 49 | 50 | .listing-container > .sidebar { 51 | background: #242831; 52 | padding: 1em; 53 | color: #5F6269; 54 | border-left: 2px solid transparent; 55 | } 56 | .listing-container.re > .sidebar { 57 | border-color: #dd4b39; 58 | } 59 | .listing-container.ml > .sidebar { 60 | border-color: #c87a27; 61 | } 62 | 63 | .listing-container > .main { 64 | width: 100%; 65 | } 66 | 67 | .listing-container > .main > pre { 68 | padding: 1em 2em; 69 | overflow: hidden; 70 | maxHeight: 45vh; 71 | } 72 | 73 | .listing-container > .main > footer { 74 | display: flex; 75 | align-items: center; 76 | justify-content: flex-end; 77 | font-size: .95em; 78 | padding: .5em; 79 | passin-tTop: 0; 80 | } 81 | .listing-container.re > .main > footer { 82 | color: #dd4b39; 83 | fill: #dd4b39; 84 | } 85 | .listing-container.ml > .main > footer { 86 | color: #c87a27; 87 | fill: #c87a27; 88 | } 89 | 90 | .listing-container > .main > footer > a { 91 | color: #51555E; 92 | margin-right: 1.5em; 93 | } 94 | 95 | .listing-container > .main > footer > a.open-button { 96 | fill: #51555E; 97 | margin-right: 1em; 98 | height: 1em; 99 | width: 1em; 100 | margin-top: 1px; /* looks a bit off-center without */ 101 | } 102 | 103 | .listing-container > .main > footer > a:hover { 104 | color: #B1B3B7; 105 | fill: #B1B3B7; 106 | cursor: pointer; 107 | } 108 | |}; 109 | -------------------------------------------------------------------------------- /src/extension/content/convert/detect.re: -------------------------------------------------------------------------------- 1 | open Core; 2 | 3 | open LocalDom; 4 | 5 | open Common; 6 | 7 | let hasClassName = (className) => List.length(getElementsByClassName(None, className)) > 0; 8 | 9 | let ocamlishRels = [|"Start", "previous", "next", "Up", "Appendix", "Section", "Subsection"|]; 10 | 11 | let hasOcamlRels = () => { 12 | let hits = 13 | getElementsByTagName(None, "link") 14 | |> List.map((link) => ocamlishRels |> Js.Array.includes(Element.getAttribute(link, "rel"))) 15 | |> List.filter(id) 16 | |> List.length; 17 | hits >= 3 18 | }; 19 | 20 | let hasCommonClassNames = () => { 21 | let hits = 22 | [ 23 | "keyword", 24 | "type", 25 | "deprecated", 26 | "mod", 27 | "modt", 28 | "typ", 29 | "spec", 30 | "def", 31 | "ext", 32 | "exn", 33 | "cls", 34 | "include", 35 | "cons", 36 | "paramstable", 37 | "sig_block" 38 | ] 39 | |> List.map(hasClassName) 40 | |> List.filter(id) 41 | |> List.length; 42 | hits >= 3 43 | }; 44 | 45 | let hasUniqueClassNames = () => 46 | ["odoc-doc", "package-index"] |> List.map(hasClassName) |> List.exists(id); 47 | 48 | let mightBeOcamlDoc = () => hasUniqueClassNames() || hasOcamlRels() || hasCommonClassNames(); 49 | 50 | let getBlacklist = () => [ 51 | "caml.inria.fr/pub/docs/u3-ocaml/ocaml-objects.html", /* #17 */ 52 | "asciidoctor.org/docs/user-manual/", /* #40 */ 53 | "ocaml.janestreet.com/ocaml-core/latest/doc/core/", /* #46 */ 54 | "mwhittaker.github.io/distributed-systems-ocaml/code_MorePipes.html" /* #49 */ 55 | ]; 56 | 57 | let getWhitelist = () => [ 58 | "codebad.com/~hdon/reason-tools-test.html" 59 | ]; 60 | 61 | let getSignificantUrl = () => Location.hostname ++ Location.pathname; 62 | 63 | let isWhitelisted = () => getWhitelist() |> List.mem(getSignificantUrl()); 64 | 65 | let isBlacklisted = () => getBlacklist() |> List.mem(getSignificantUrl()); 66 | 67 | let shouldConvert = () => { 68 | let cached: option(bool) = [%raw "window._rtShouldConvert"] |> Js.Undefined.toOption; 69 | switch cached { 70 | | Some(shouldConvert) => shouldConvert 71 | | None => 72 | let isWhitelisted = isWhitelisted(); 73 | let mightBeOcamlDoc = mightBeOcamlDoc(); 74 | let isBlacklisted = isBlacklisted(); 75 | let shouldConvert = isWhitelisted || mightBeOcamlDoc && ! isBlacklisted; 76 | /* 77 | Js.log @@ "isWhitelisted: " ^ (string_of_bool isWhitelisted); 78 | Js.log @@ "mightBeOcamlDoc: " ^ (string_of_bool mightBeOcamlDoc); 79 | Js.log @@ "isBlackListed: " ^ (string_of_bool isBlacklisted); 80 | Js.log @@ "shouldConvert: " ^ (string_of_bool shouldConvert); 81 | */ 82 | if (shouldConvert) { 83 | ignore([%raw "window._rtShouldConvert = 1"]) 84 | } else { 85 | ignore([%raw "window._rtShouldConvert = 0"]) 86 | }; 87 | shouldConvert 88 | } 89 | }; 90 | -------------------------------------------------------------------------------- /docs/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Reason Tools 5 | 90 | 91 | 92 | 93 | 94 | 95 |
96 | 97 | 98 | -------------------------------------------------------------------------------- /src/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Reason Tools 5 | 90 | 91 | 92 | 93 | 94 | 95 |
96 | 97 | 98 | -------------------------------------------------------------------------------- /src/extension/common/chrome.re: -------------------------------------------------------------------------------- 1 | module BrowserAction = { 2 | [@bs.val] external setIcon : Js.t({..}) => unit = "chrome.browserAction.setIcon"; 3 | }; 4 | 5 | module Extension = { 6 | [@bs.val] external getURL : string => string = "chrome.extension.getURL"; 7 | }; 8 | 9 | module Runtime = { 10 | [@bs.val] external sendMessage : ('a, 'b) => unit = "chrome.runtime.sendMessage"; 11 | [@bs.val] 12 | external addMessageListener : (('a, Js.t({..}), 'b => unit) => unit) => unit = 13 | "chrome.runtime.onMessage.addListener"; 14 | }; 15 | 16 | module Storage = { 17 | module Local = { 18 | [@bs.val] external get : (string, 'a => unit) => unit = "chrome.storage.local.get"; 19 | [@bs.val] external set : Js.t({..}) => unit = "chrome.storage.local.set"; 20 | }; 21 | [@bs.val] 22 | external addChangeListener : 23 | ( 24 | ( 25 | Js.Dict.t( 26 | { 27 | . 28 | "newValue": 'a, 29 | "oldValue": 'a 30 | } 31 | ), 32 | string 33 | ) => 34 | unit 35 | ) => 36 | unit = 37 | "chrome.storage.onChanged.addListener"; 38 | }; 39 | 40 | module Commands = { 41 | [@bs.val] 42 | external addListener : (string => unit) => unit = "chrome.commands.onCommand.addListener"; 43 | }; 44 | 45 | module Tabs = { 46 | type tabId; 47 | /* 48 | type tab = Js.t {. 49 | id: tabId 50 | }; 51 | */ 52 | [@bs.val] external create : {. "url": string} => unit = "chrome.tabs.create"; 53 | [@bs.val] external update : (int, Js.t({..})) => unit = "chrome.tabs.update"; 54 | /* TODO: Need MaybeArray to work because Chrome will return an array, but FF supposedly does not */ 55 | /*external executeScript : Js.t {. code: string } => (MaybeArray.t (Js.t {..}) => unit) => unit = "chrome.tabs.executeScript" [@@bs.val];*/ 56 | [@bs.val] 57 | external executeScript : ({. "code": string}, Js.null_undefined(array(string)) => unit) => unit = 58 | "chrome.tabs.executeScript"; 59 | [@bs.val] 60 | external executeScriptFile : ({. "file": string}, unit => unit) => unit = 61 | "chrome.tabs.executeScript"; 62 | /* TODO: could use bs.ginore here? */ 63 | [@bs.val] external sendMessage : (tabId, 'a, 'b) => unit = "chrome.tabs.sendMessage"; 64 | [@bs.val] 65 | external query : 66 | ( 67 | Js.t({..}), 68 | array( 69 | { 70 | . 71 | "url": string, 72 | "id": int 73 | } 74 | ) => 75 | unit 76 | ) => 77 | unit = 78 | "chrome.tabs.query"; 79 | }; 80 | 81 | module ContextMenus = { 82 | type id; 83 | /* 84 | type config = Js.t {. 85 | title: string, 86 | context: array string, 87 | onclick: (unit => Tabs.tab => unit) 88 | }; 89 | */ 90 | [@bs.val] external create : /*config*/ Js.t({..}) => id = "chrome.contextMenus.create"; 91 | [@bs.val] external update : (id, /*config*/ Js.t({..})) => unit = "chrome.contextMenus.update"; 92 | }; 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reason Tools 2 | 3 | Adds [Reason](http://reasonml.github.io/) to the browser. 4 | 5 | ## Getting started 6 | 7 | Directly in the browser: https://reasonml.github.io/reason-tools/popup.html 8 | 9 | Or, browser extensions: [Chrome](https://chrome.google.com/webstore/detail/reason-tools/kmdelnjbembbiodplmhgfjpecibfhadd) | [Firefox](https://addons.mozilla.org/en-US/firefox/addon/reason-tools/) 10 | 11 | ![demo of reason-tools](https://raw.githubusercontent.com/rickyvetter/reason-tools/master/assets/demo.gif) 12 | 13 | When you trigger the extension, it will copy the text you have highlighted and put it into an editor. The text is then translated into the corresponding Reason/OCaml text. Reason Tools will automatically convert between `.re`, `.ml`, `.rei`, and `.mli` text. In browsers that support it (currently just Chrome), a shortcut is added to open the extension with the highlighted text: `Alt+D`. 14 | 15 | ![before and after of auto conversion](https://cloud.githubusercontent.com/assets/1909539/21284240/f5828a68-c3ca-11e6-9e29-13cf1a4f05fa.png) 16 | 17 | ### OCaml Documentation 18 | 19 | Reason Tools tries to detect OCaml documentation pages and automatically convert between Reason and OCaml syntaxes. It provides an escape hatch for use if there is a false positive detection, or if you prefer the default OCamlDoc styles. 20 | 21 | ### Github Integration 22 | 23 | Reason Tools adds a shortcut from interface to implementation files (`.re` <=> `.rei`, `.ml` <=> `.mli`) on Github when it's detected. Triggered by `Alt+I` by default (> v0.0.19). 24 | 25 | ## Contribute 26 | 27 | To get started contributing you can clone and build the extension: 28 | 29 | ```sh 30 | git clone https://github.com/rickyvetter/reason-tools.git 31 | cd reason-tools 32 | npm install # this will take a few minutes 33 | ``` 34 | 35 | `npm run build` to build the project. You also have the granular steps `build:self` and `build:js` for faster iteration. 36 | 37 | To load in Chrome, go to `chrome://extensions/` and turn on Developer Mode. From there you should be able to select "Load unpacked extension..." and choose `reason-tools/_build/extension`. 38 | 39 | ### Build Systems 40 | 41 | This project is a bit unconventional in nature (compiling a compiler and a parser/printer to web), so it uses a few build systems currently. 42 | 43 | - A custom `shell.sh` is used to compile Reason and the compiler to JavaScript. 44 | - [Bsb](http://bucklescript.github.io/bucklescript/Manual.html#_build_system_support) is used to build its Reason logic. Nothing special. 45 | - [Webpack](http://webpack.github.io/) is used to bundle the whole js output into a single pack. 46 | 47 | ## Thanks 48 | 49 | The foundation of the project is, without a doubt, [refmt-web](https://github.com/Schmavery/refmt-web). This is an awesome project by @Schmavery which does the same refmt in a web page. 50 | 51 | [reason-web-toplevel](https://github.com/Engil/reason-web-toplevel), by @Engil was also an awesome project where a lot of the work in this project came from. 52 | 53 | Also huge thanks to the [js_of_ocaml](https://github.com/ocsigen/js_of_ocaml) team for building a compiler that pretty effortlessly builds Reason and refmt utils in JS. 54 | -------------------------------------------------------------------------------- /src/extension/background.re: -------------------------------------------------------------------------------- 1 | open Core; 2 | 3 | module Refmt = { 4 | let refmt = Refmt2.refmt; 5 | let parse = 6 | fun 7 | | Result.Error(error) => Result.Error(error) 8 | | Result.Ok((inLang, outLang, outText)) => 9 | Result.Ok( 10 | Protocol.Refmt.{ 11 | outText, 12 | inLang, 13 | outLang 14 | } 15 | ) 16 | ; 17 | }; 18 | 19 | Protocol.Refmt.listen( 20 | ({input, inLang, inType, outLang}, respond) => 21 | Refmt.refmt(input, inLang, inType, outLang) |> Refmt.parse |> respond 22 | ); 23 | 24 | Protocol.OpenInTab.listen((text) => Chrome.Tabs.create({"url": "popup.html#" ++ Util.btoa(text)})); 25 | 26 | let loadContentScripts = (tabId, callback) => 27 | Chrome.Tabs.executeScriptFile( 28 | {"file": "Content.bundle.js"}, 29 | () => { 30 | Protocol.NotifyLoaded.send(tabId); 31 | callback() 32 | } 33 | ); 34 | 35 | let ensureLoaded = (tabId, callback) => 36 | Protocol.QueryLoaded.query( 37 | tabId, 38 | (loaded) => 39 | if (! loaded) { 40 | loadContentScripts(tabId, callback) 41 | } else { 42 | callback() 43 | } 44 | ); 45 | 46 | Protocol.LoadScripts.listen((tabId) => loadContentScripts(tabId, noop)); 47 | 48 | let refmtSelection = (tabId) => ensureLoaded(tabId, () => Protocol.RefmtSelection.send(tabId)); 49 | 50 | let toggleConversion = (tabId) => ensureLoaded(tabId, () => Protocol.ToggleConversion.send(tabId)); 51 | 52 | Chrome.ContextMenus.create({ 53 | "title": "Refmt", 54 | "contexts": [|"selection"|], 55 | "onclick": (_, tab) => refmtSelection(tab##id) 56 | }); 57 | 58 | Chrome.ContextMenus.create({ 59 | "title": "Toggle", 60 | "contexts": [|"browser_action", "page"|], 61 | "onclick": (_, tab) => toggleConversion(tab##id) 62 | }); 63 | 64 | Chrome.Commands.addListener( 65 | (command) => 66 | if (command == "toggle_between_interface_and_implementation") { 67 | Chrome.Tabs.query( 68 | {"active": Js.true_, "currentWindow": Js.true_}, 69 | (activeTabs) => { 70 | let mapExtension = 71 | fun 72 | | "ml" => "mli" 73 | | "mli" => "ml" 74 | | "re" => "rei" 75 | | "rei" => "re" 76 | | ext => ext; 77 | let convertBetweenInterfaceAndImplementation = (url) => 78 | switch (Js.Array.pop @@ Js.String.split(".", url)) { 79 | | None => url 80 | | Some(ext) => Js.String.replace("." ++ ext, "." ++ mapExtension(ext), url) 81 | }; 82 | let currentTab = activeTabs[0]; 83 | let url = currentTab##url; 84 | let newURL = convertBetweenInterfaceAndImplementation(url); 85 | if (newURL != url) { 86 | Chrome.Tabs.update(currentTab##id, {"url": newURL}) 87 | } 88 | } 89 | ) 90 | } 91 | ); 92 | 93 | let enabledIconSet = {"19": "logo19.png", "38": "logo38.png"}; 94 | 95 | let disabledIconSet = {"19": "logo19_gray.png", "38": "logo38_gray.png"}; 96 | 97 | Protocol.Storage.queryDisabled( 98 | (disabled) => { 99 | let set = (value, ()) => Protocol.Storage.setDisabled(value); 100 | let id = 101 | Chrome.ContextMenus.create({ 102 | "title": disabled ? "Enable" : "Disable", 103 | "contexts": [|"browser_action"|], 104 | "onclick": set(! disabled) 105 | }); 106 | Chrome.BrowserAction.setIcon({"path": disabled ? disabledIconSet : enabledIconSet}); 107 | Protocol.Storage.onDisabledChanged( 108 | (disabled) => { 109 | Chrome.ContextMenus.update( 110 | id, 111 | {"title": disabled ? "Enable" : "Disable", "onclick": set(! disabled)} 112 | ); 113 | Chrome.BrowserAction.setIcon({"path": disabled ? disabledIconSet : enabledIconSet}) 114 | } 115 | ) 116 | } 117 | ); 118 | -------------------------------------------------------------------------------- /src/extension/content/convert/retrieve.re: -------------------------------------------------------------------------------- 1 | open LocalDom; 2 | 3 | open Common; 4 | 5 | type typeTable = { 6 | el: Element.t, 7 | text: string, 8 | remove: unit => unit 9 | }; 10 | 11 | type listing = { 12 | els: list(Element.t), 13 | text: string, 14 | replace: string => Element.t 15 | }; 16 | 17 | let getTypeTable = (pre) => 18 | switch (Js.Null.toOption(Element.nextElementSibling(pre))) { 19 | | None => None 20 | | Some(el) => 21 | if (Js.to_bool(DOMTokenList.contains(Element.classList(el), "typetable"))) { 22 | let text = Element.innerText(el); 23 | switch (Js.Null.toOption(Element.nextSibling(el))) { 24 | | None => Some({el, text, remove: () => Element.remove(el)}) 25 | | Some(next) => 26 | if (Node.nodeType(next) == Node._TEXT_NODE) { 27 | Some({ 28 | el, 29 | text: text ++ Node.nodeValue(next), 30 | remove: () => { 31 | Element.remove(el); 32 | Node.removeChild(Node.parentNode(next), next) 33 | } 34 | }) 35 | } else { 36 | Some({el, text, remove: () => Element.remove(el)}) 37 | } 38 | } 39 | } else { 40 | None 41 | } 42 | }; 43 | 44 | let getModuleSignature = (pre) => 45 | switch (Js.String.endsWith("sig .. end", Element.innerText(pre))) { 46 | | false => None 47 | | true => 48 | let anchor = LocalDom.Arrayish.toArray( 49 | Element.getElementsByTagName(pre, "a") 50 | )[0]; 51 | let anchor = Element.outerHTML(anchor); 52 | let re = [%re "/sig .. end/g"]; 53 | let replaced = Js.String.unsafeReplaceBy0( 54 | re, 55 | (_, _, _) => "{ " ++ anchor ++ " }", 56 | Element.innerText(pre) 57 | ); 58 | Some({ 59 | els: [pre], 60 | text: "", 61 | replace: (_html) => { 62 | Element.setInnerHTML(pre, replaced); 63 | pre 64 | } 65 | }) 66 | }; 67 | 68 | let getPreListings = () => 69 | getElementsByTagName(None, "pre") 70 | |> List.map( 71 | (el) => 72 | switch (getTypeTable(el), getModuleSignature(el)) { 73 | | (Some(typeTable), _) => { 74 | els: [el, typeTable.el], 75 | text: Element.innerText(el) ++ typeTable.text, 76 | replace: (html) => { 77 | Element.setInnerHTML(el, html); 78 | typeTable.remove(); 79 | el 80 | } 81 | } 82 | | (_, Some(preListing)) => preListing 83 | | (None, None) => { 84 | els: [el], 85 | text: Element.innerText(el), 86 | replace: (html) => { 87 | Element.setInnerHTML(el, html); 88 | el 89 | } 90 | } 91 | } 92 | ); 93 | 94 | let getDefListings = () => 95 | getElementsByClassName(None, "def") 96 | |> List.map( 97 | (el) => { 98 | els: [el], 99 | text: Element.innerText(el), 100 | replace: (html) => { 101 | Element.setInnerHTML(el, "
" ++ html ++ "
"); 102 | el 103 | } 104 | } 105 | ); 106 | 107 | let getLstListings = () => 108 | getElementsByClassName(None, "lstlisting") 109 | |> List.map( 110 | (el) => { 111 | els: [el], 112 | text: Element.innerText(el), 113 | replace: (html) => { 114 | let parent = Node.parentNode(el); 115 | Element.setInnerHTML(parent, "
" ++ html ++ "
"); 116 | parent 117 | } 118 | } 119 | ); 120 | 121 | let getCodeListings = () => 122 | getElementsByTagName(None, "code") 123 | |> List.map( 124 | (el) => { 125 | els: [el], 126 | text: Element.innerText(el), 127 | replace: (html) => { 128 | Element.setInnerHTML(el, html); 129 | el 130 | } 131 | } 132 | ); 133 | 134 | let getListings = () => 135 | [getPreListings, getDefListings, getLstListings, getCodeListings] 136 | |> List.map((f) => f()) 137 | |> List.flatten; 138 | -------------------------------------------------------------------------------- /src/extension/common/protocol.re: -------------------------------------------------------------------------------- 1 | open Common; 2 | open Result; 3 | 4 | type language = RefmtShared.language; 5 | 6 | type codeType = RefmtShared.codeType; 7 | 8 | let stringOfLanguage = RefmtShared.stringOfLanguage; 9 | 10 | let languageOfString = RefmtShared.languageOfString; 11 | 12 | let stringOfType = RefmtShared.stringOfType; 13 | 14 | let stringOfLanguageHuman = (lang: language) => 15 | switch lang { 16 | | ML => "ML" 17 | | RE => "RE" 18 | | REO => "RE v1" 19 | | UnknownLang => "Unkown" 20 | }; 21 | 22 | module Refmt = { 23 | exception DeserializationFail; 24 | type request = { 25 | input: string, 26 | inLang: language, 27 | inType: codeType, 28 | outLang: language 29 | }; 30 | [@bs.deriving jsConverter] 31 | type payload = { 32 | outText: string, 33 | inLang: language, 34 | outLang: language 35 | }; 36 | type response = result(string, payload); 37 | let responseToJs = x => { 38 | switch (x) { 39 | | Result.Ok(x) => Obj.magic(("Ok", payloadToJs(x))) 40 | | Result.Error(x) => Obj.magic(("Error", x)) 41 | }; 42 | }; 43 | let responseFromJs:Js.Json.t=>response = x => { 44 | switch(Obj.magic(x)) { 45 | | ("Ok", x) => Result.Ok(payloadFromJs(Obj.magic(x))) 46 | | (_, x) => Result.Error(Obj.magic(x)) 47 | }; 48 | }; 49 | /* Bucklescript's variant tags will be erased when serialized, so we have to manually serialize the response 50 | */ 51 | let send = 52 | ( 53 | text, 54 | ~inLang=RefmtShared.UnknownLang, 55 | ~inType=RefmtShared.UnknownType, 56 | ~outLang=RefmtShared.UnknownLang, 57 | cb:(response)=>unit 58 | ) => 59 | Message.query( 60 | "refmt:refmt", 61 | {input: text |> normalizeText |> untoplevel, inLang, inType, outLang}, 62 | (response) => cb(responseFromJs(response)) 63 | ); 64 | let listen: ((request, response => unit) => unit) => unit = 65 | (cb) => 66 | Message.receive( 67 | "refmt:refmt", 68 | (request, _, respond) => cb(request, (r) => { 69 | r |> responseToJs |> respond; 70 | }) 71 | ); 72 | }; 73 | 74 | module OpenInTab = { 75 | let send = (text) => Message.send("background:open", text); 76 | let listen = (callback) => Message.receive("background:open", (text, _, _) => callback(text)); 77 | }; 78 | 79 | module ToggleConversion = { 80 | let send = (tabId) => Message.sendTab(tabId, "content:toggle", ()); 81 | let listen = (callback) => Message.receive("content:toggle", (_, _, _) => callback()); 82 | }; 83 | 84 | module RefmtSelection = { 85 | let send = (tabId) => Message.sendTab(tabId, "content:refmt-selection", ()); 86 | let listen = (callback) => Message.receive("content:refmt-selection", (_, _, _) => callback()); 87 | }; 88 | 89 | module LoadScripts = { 90 | let send = () => Message.send("background:load-content-scripts", ()); 91 | let listen = (callback) => 92 | Message.receive( 93 | "background:load-content-scripts", 94 | (_, sender, _) => callback(sender##tab##id) 95 | ); 96 | }; 97 | 98 | module NotifyLoaded = { 99 | let send = (tabId) => Message.sendTab(tabId, "content:notify-loaded", ()); 100 | let listen = (callback) => Message.receive("content:notify-loaded", (_, _, _) => callback()); 101 | }; 102 | 103 | module QueryLoaded = { 104 | let query = (tabId, callback) => Message.queryTab(tabId, "content:query-loaded", (), callback); 105 | let listen = (callback) => 106 | Message.receive("content:query-loaded", (_, _, respond) => respond(callback())); 107 | }; 108 | 109 | module Storage = { 110 | let queryDisabled = (callback: bool => unit) => 111 | Chrome.Storage.Local.get( 112 | "disabled", 113 | (response) => response##disabled |> Js.Undefined.toOption |> Js.Option.getWithDefault(false) |> callback 114 | ); 115 | let setDisabled = (value: bool) => Chrome.Storage.Local.set({"disabled": value}); 116 | let onDisabledChanged = (callback: bool => unit) => 117 | Chrome.Storage.addChangeListener( 118 | (changes, _) => 119 | switch (Js.Dict.get(changes, "disabled")) { 120 | | Some(change) => callback(change##newValue) 121 | | None => () 122 | } 123 | ); 124 | let queryLatestInput = (callback: option(string) => unit) => 125 | Chrome.Storage.Local.get( 126 | "latestRefmtString", 127 | (response) => response##latestRefmtString |> Js.Null_undefined.to_opt |> callback 128 | ); 129 | let setLatestInput = (value: string) => Chrome.Storage.Local.set({"latestRefmtString": value}); 130 | }; 131 | -------------------------------------------------------------------------------- /src/extension/content/overlay.re: -------------------------------------------------------------------------------- 1 | open LocalDom; 2 | 3 | open Common; 4 | 5 | let renderInTheShadows = (renderer) => { 6 | let host = Document.createElement("div"); 7 | let shadow = Element.attachShadow(host, {"mode": "closed"}); 8 | let remove = () => Body.removeChild(host); 9 | Body.appendChild(host); 10 | /* React must render directly to the shadow root, otherwise onclick handlers won't work */ 11 | ReactDOMRe.render(renderer(remove), Element.toReasonJsElement(shadow)); 12 | /* Only after React has rendered can we attach the stylesheets, otherwise React will render over them */ 13 | let style = Document.createElement("style"); 14 | Element.setType(style, "text/css"); 15 | Element.setInnerText(style, InlineStyles.stylesheet); 16 | Element.appendChild(shadow, createStylesheet()); 17 | Element.appendChild(shadow, style) 18 | }; 19 | 20 | let showOverlay = (inLang, inText, outLang, outText) => 21 | renderInTheShadows( 22 | (remove) => 23 | 24 | ); 25 | 26 | let showError = (message) => renderInTheShadows((remove) => ); 27 | 28 | let try_ = (text) => 29 | Protocol.Refmt.send( 30 | text, 31 | (response) => 32 | switch response { 33 | | Result.Error(message) => showError(message) 34 | | Result.Ok({outText, inLang, outLang}) => showOverlay(inLang, text, outLang, outText) 35 | } 36 | ); 37 | 38 | let ocamlLogo = [%bs.raw {|require('../../../../../src/images/ocamlLogo128.png')|}]; 39 | 40 | let reasonLogo = [%bs.raw {|require('../../../../../src/images/logo128.png')|}]; 41 | 42 | let addStyleSheet = () => { 43 | let element = Document.createElement("style"); 44 | Element.setType(element, "text/css"); 45 | Element.setInnerText( 46 | element, 47 | {| 48 | .reason_tools_button.reason_tools_button.reason_tools_button { 49 | position: fixed; 50 | right: 0; 51 | height: 50px; 52 | width: 50px; 53 | background-color: black; 54 | color: white; 55 | font-family: monospace; 56 | display: flex; 57 | justify-content: center; 58 | align-items: center; 59 | font-weight: 900; 60 | opacity: 0.6; 61 | } 62 | .reason_tools_button.reason_tools_button.reason_tools_button:hover { 63 | opacity: 1; 64 | cursor: pointer; 65 | } 66 | .reason_tools_anchor { 67 | color: #cec47f; 68 | } 69 | .reason_tools_anchor:before { 70 | content: ''; 71 | float: left; 72 | position: relative; 73 | width: 0; 74 | height: 50px; 75 | margin-top: -50px; 76 | } 77 | .reason_tools_anchor:target:after { 78 | content: ''; 79 | position: relative; 80 | width: 4px; 81 | margin-left: -4px; 82 | height: 18px; 83 | float: left; 84 | background-color: #97B98c; 85 | left: -10px; 86 | } 87 | |} 88 | ); 89 | Body.appendChild(element) 90 | }; 91 | 92 | let updateSyntaxSwapButton = () => { 93 | let element = Document.getElementById("syntax-swap-button"); 94 | let style = Element.style(element); 95 | let logo = 96 | Style.backgroundImage(style) |> Js.String.includes(reasonLogo) ? ocamlLogo : reasonLogo; 97 | let url = Chrome.Extension.getURL(logo); 98 | Style.setBackgroundImage(style, {j|url($url)|j}) 99 | }; 100 | 101 | let addSyntaxSwapButton = (swap) => { 102 | open Element; 103 | let element = Document.createElement("div"); 104 | Style.setTop(style(element), "40px"); 105 | setId(element, "syntax-swap-button"); 106 | setTitle(element, "Swap between OCaml and Reason syntax"); 107 | setClassName(element, "reason_tools_button"); 108 | setOnClick(element, (_) => swap(`not_initial)); 109 | Style.setBackgroundImage(style(element), "url(" ++ Chrome.Extension.getURL(reasonLogo) ++ ")"); 110 | Style.setBackgroundSize(style(element), "cover"); 111 | Body.appendChild(element) 112 | }; 113 | 114 | let addStyleSwapButton = (swap) => { 115 | open Element; 116 | let element = Document.createElement("div"); 117 | Style.setTop(style(element), "90px"); 118 | setInnerText(element, ""); 119 | setTitle(element, "Swap between custom and original stylesheet"); 120 | setClassName(element, "reason_tools_button"); 121 | setOnClick(element, swap); 122 | Body.appendChild(element) 123 | }; 124 | 125 | let addSwapButtons = (swapStyleSheets, swapSyntax) => { 126 | addStyleSheet(); 127 | addSyntaxSwapButton(swapSyntax); 128 | addStyleSwapButton(swapStyleSheets) 129 | }; 130 | 131 | let toggle = (swapStyleSheets, swapSyntax) => { 132 | let buttons = Document.getElementsByClassName("reason_tools_button") |> Arrayish.toArray; 133 | if (Array.length(buttons) > 0) { 134 | buttons |> Array.iter(Element.remove) 135 | } else { 136 | addSwapButtons(swapStyleSheets, swapSyntax) 137 | } 138 | }; 139 | -------------------------------------------------------------------------------- /src/extension/common/localDom.re: -------------------------------------------------------------------------------- 1 | module Arrayish = { 2 | type t('a); 3 | [@bs.val] external toArray : t('a) => array('a) = "Array.prototype.slice.call"; 4 | }; 5 | 6 | module DOMTokenList = { 7 | type t; 8 | [@bs.send] external contains : (t, string) => Js.boolean = "contains"; 9 | }; 10 | 11 | module Style = { 12 | type t; 13 | [@bs.get] external backgroundImage : t => string = ""; 14 | [@bs.set] external setBackgroundImage : (t, string) => unit = "backgroundImage"; 15 | [@bs.set] external setBackgroundSize : (t, string) => unit = "backgroundSize"; 16 | [@bs.set] external setTop : (t, string) => unit = "top"; 17 | }; 18 | 19 | module Element = { 20 | type t; 21 | [@bs.get] external classList : t => DOMTokenList.t = "classList"; 22 | [@bs.get] external value : Dom.element => string = "value"; 23 | [@bs.set] external setClassName : (t, string) => unit = "className"; 24 | [@bs.get] external href : t => string = "href"; 25 | [@bs.set] external setHref : (t, string) => unit = "href"; 26 | [@bs.get] external id : t => string = "id"; 27 | [@bs.set] external setId : (t, string) => unit = "id"; 28 | [@bs.set] external setInnerHTML : (t, string) => unit = "innerHTML"; 29 | [@bs.get] external outerHTML : t => string = "outerHTML"; 30 | [@bs.get] external innerText : t => string = "innerText"; 31 | [@bs.set] external setInnerText : (t, string) => unit = "innerText"; 32 | [@bs.set] external setTitle : (t, string) => unit = "title"; 33 | [@bs.get] external nextSibling : t => Js.null(t) = "nextSibling"; 34 | [@bs.get] external nextElementSibling : t => Js.null(t) = "nextElementSibling"; 35 | [@bs.set] external setRel : (t, string) => unit = "rel"; 36 | [@bs.get] external style : t => Style.t = "style"; 37 | [@bs.set] external setStyle : (t, string) => unit = "style"; 38 | [@bs.set] external setType : (t, string) => unit = "type"; 39 | [@bs.set] external setOnClick : (t, Js.t({..}) => unit) => unit = "onclick"; 40 | [@bs.send] external appendChild : (t, t) => unit = ""; 41 | [@bs.send] external getAttribute : (t, string) => string = "getAttribute"; 42 | [@bs.send] external getElementsByClassName : (t, string) => Arrayish.t(t) = ""; 43 | [@bs.send] external getElementsByTagName : (t, string) => Arrayish.t(t) = ""; 44 | [@bs.send] external remove : t => unit = "remove"; 45 | [@bs.send] external querySelectorAll : (t, string) => Arrayish.t(t) = ""; 46 | [@bs.send] external attachShadow : (t, Js.t({..})) => t = ""; 47 | external toReasonJsElement : t => Dom.element = "%identity"; 48 | }; 49 | 50 | module Node = { 51 | let _TEXT_NODE = 3; 52 | let _DOCUMENT_POSITION_PRECEDING = 2; 53 | let _DOCUMENT_POSITION_CONTAINS = 8; 54 | [@bs.get] external nodeType : Element.t => int = ""; 55 | [@bs.get] external nodeValue : Element.t => string = ""; 56 | [@bs.get] external parentNode : Element.t => Element.t = ""; 57 | [@bs.send] external cloneNode : Element.t => Element.t = ""; 58 | [@bs.send] external compareDocumentPosition : (Element.t, Element.t) => int = ""; 59 | [@bs.send] 60 | external insertBefore : (~target: Element.t, ~new_: Element.t, ~ref_: Element.t) => unit = ""; 61 | [@bs.send] external removeChild : (Element.t, Element.t) => unit = ""; 62 | }; 63 | 64 | module Document = { 65 | type t; 66 | [@bs.val] 67 | external addEventListener : (string, unit => unit) => unit = "document.addEventListener"; 68 | [@bs.val] external createElement : string => Element.t = "document.createElement"; 69 | [@bs.val] 70 | external getElementsByClassName : string => Arrayish.t(Element.t) = 71 | "document.getElementsByClassName"; 72 | [@bs.val] external getElementById : string => Element.t = "document.getElementById"; 73 | [@bs.val] 74 | external getElementsByTagName : string => Arrayish.t(Element.t) = 75 | "document.getElementsByTagName"; 76 | }; 77 | 78 | module Location = { 79 | [@bs.val] external hash : string = "window.location.hash"; 80 | [@bs.val] external hostname : string = "window.location.hostname"; 81 | [@bs.val] external pathname : string = "window.location.pathname"; 82 | [@bs.val] external reload : Js.boolean => unit = "window.location.reload"; 83 | }; 84 | 85 | module Body = { 86 | [@bs.val] external outerHTML : string = "document.body.outerHTML"; 87 | [@bs.val] external appendChild : Element.t => unit = "document.body.appendChild"; 88 | [@bs.val] external removeChild : Element.t => unit = "document.body.removeChild"; 89 | }; 90 | 91 | module Head = { 92 | [@bs.val] external appendChild : Element.t => unit = "document.head.appendChild"; 93 | }; 94 | 95 | module Selection = { 96 | type t; 97 | [@bs.get] external anchorNode : t => Element.t = ""; 98 | [@bs.get] external focusNode : t => Element.t = ""; 99 | [@bs.send] external toString : t => string = ""; 100 | [@bs.send] external removeAllRanges : t => unit = ""; 101 | }; 102 | 103 | module Window = { 104 | [@bs.val] external getSelection : unit => Selection.t = "window.getSelection"; 105 | }; 106 | -------------------------------------------------------------------------------- /src/extension/popup/popupWindow.re: -------------------------------------------------------------------------------- 1 | type action = 2 | | LinkCopyConfirmation 3 | | TextCopyConfirmation 4 | | RemoveCopyConfirmation 5 | | InLanguageChange(RefmtShared.language) 6 | | OutLanguageChange(RefmtShared.language); 7 | 8 | let select = (name, onChange, language, lang) => 9 | ; 28 | 29 | type state = { 30 | copyConfirmation: string, 31 | inLanguage: Protocol.language, 32 | outLanguage: Protocol.language, 33 | dialogKillTimer: ref(option(Js.Global.timeoutId)) 34 | }; 35 | 36 | let resetTimer = ({ReasonReact.state, reduce}) => { 37 | switch state.dialogKillTimer^ { 38 | | None => () 39 | | Some(timer) => Js.Global.clearTimeout(timer) 40 | }; 41 | state.dialogKillTimer := Some(Js.Global.setTimeout(reduce(() => RemoveCopyConfirmation), 2500)) 42 | }; 43 | 44 | let component = ReasonReact.reducerComponent("PopupWindow"); 45 | 46 | let make = 47 | ( 48 | ~inText, 49 | ~inLang, 50 | ~outText, 51 | ~outLang, 52 | ~link, 53 | ~onOpen, 54 | ~onInputChanged: 55 | (~inLang: Protocol.language=?, ~outLang: Protocol.language=?, string) => unit, 56 | _ 57 | ) => { 58 | ...component, 59 | reducer: (action, state) => 60 | switch action { 61 | | LinkCopyConfirmation => 62 | ReasonReact.UpdateWithSideEffects( 63 | {...state, copyConfirmation: "Link copied to clipboard"}, 64 | resetTimer 65 | ) 66 | | TextCopyConfirmation => 67 | ReasonReact.UpdateWithSideEffects( 68 | {...state, copyConfirmation: "Text copied to clipboard"}, 69 | resetTimer 70 | ) 71 | | RemoveCopyConfirmation => ReasonReact.Update({...state, copyConfirmation: ""}) 72 | | InLanguageChange(lang) => 73 | ReasonReact.UpdateWithSideEffects( 74 | {...state, inLanguage: lang}, 75 | ((self) => onInputChanged(~inLang=lang, ~outLang=self.state.outLanguage, inText)) 76 | ) 77 | | OutLanguageChange(lang) => 78 | ReasonReact.UpdateWithSideEffects( 79 | {...state, outLanguage: lang}, 80 | ((self) => onInputChanged(~inLang=self.state.inLanguage, ~outLang=lang, inText)) 81 | ) 82 | }, 83 | initialState: () => { 84 | copyConfirmation: "", 85 | inLanguage: RefmtShared.UnknownLang, 86 | outLanguage: RefmtShared.UnknownLang, 87 | dialogKillTimer: ref(None) 88 | }, 89 | render: ({state, reduce, handle}) => { 90 | Js.log(inText); 91 | let inLanguageChange = (event) => { 92 | let lang = 93 | event 94 | |> ReactEventRe.Synthetic.target 95 | |> LocalDom.Element.value 96 | |> Protocol.languageOfString; 97 | InLanguageChange(lang) 98 | }; 99 | let outLanguageChange = (event) => { 100 | let lang = 101 | event 102 | |> ReactEventRe.Synthetic.target 103 | |> LocalDom.Element.value 104 | |> Protocol.languageOfString; 105 | OutLanguageChange(lang) 106 | }; 107 | let handleInputChange = (input, {ReasonReact.state}) => 108 | onInputChanged(~inLang=state.inLanguage, ~outLang=state.outLanguage, input); 109 |
110 |
111 |

112 | 116 |

117 | CodeMirror.execCommand(editor, "selectAll")} 122 | onChange=(handle(handleInputChange)) 123 | /> 124 |
125 |
126 |

127 | 131 | LinkCopyConfirmation)) 136 | /> 137 | TextCopyConfirmation)) 141 | /> 142 | onOpen(inText)) /> 143 |

144 | 145 | 150 |
151 |
152 | } 153 | }; 154 | -------------------------------------------------------------------------------- /src/images/ocamlLogo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 15 | 16 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 85 | 86 | 87 | 88 | 89 | 90 | 96 | 101 | 107 | 111 | 112 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /docs/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: 300px; 7 | color: black; 8 | } 9 | 10 | /* PADDING */ 11 | 12 | .CodeMirror-lines { 13 | padding: 4px 0; /* Vertical padding around content */ 14 | } 15 | .CodeMirror pre { 16 | padding: 0 4px; /* Horizontal padding of content */ 17 | } 18 | 19 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 20 | background-color: white; /* The little square between H and V scrollbars */ 21 | } 22 | 23 | /* GUTTER */ 24 | 25 | .CodeMirror-gutters { 26 | border-right: 1px solid #ddd; 27 | background-color: #f7f7f7; 28 | white-space: nowrap; 29 | } 30 | .CodeMirror-linenumbers {} 31 | .CodeMirror-linenumber { 32 | padding: 0 3px 0 5px; 33 | min-width: 20px; 34 | text-align: right; 35 | color: #999; 36 | white-space: nowrap; 37 | } 38 | 39 | .CodeMirror-guttermarker { color: black; } 40 | .CodeMirror-guttermarker-subtle { color: #999; } 41 | 42 | /* CURSOR */ 43 | 44 | .CodeMirror-cursor { 45 | border-left: 1px solid black; 46 | border-right: none; 47 | width: 0; 48 | } 49 | /* Shown when moving in bi-directional text */ 50 | .CodeMirror div.CodeMirror-secondarycursor { 51 | border-left: 1px solid silver; 52 | } 53 | .cm-fat-cursor .CodeMirror-cursor { 54 | width: auto; 55 | border: 0 !important; 56 | background: #7e7; 57 | } 58 | .cm-fat-cursor div.CodeMirror-cursors { 59 | z-index: 1; 60 | } 61 | 62 | .cm-animate-fat-cursor { 63 | width: auto; 64 | border: 0; 65 | -webkit-animation: blink 1.06s steps(1) infinite; 66 | -moz-animation: blink 1.06s steps(1) infinite; 67 | animation: blink 1.06s steps(1) infinite; 68 | background-color: #7e7; 69 | } 70 | @-moz-keyframes blink { 71 | 0% {} 72 | 50% { background-color: transparent; } 73 | 100% {} 74 | } 75 | @-webkit-keyframes blink { 76 | 0% {} 77 | 50% { background-color: transparent; } 78 | 100% {} 79 | } 80 | @keyframes blink { 81 | 0% {} 82 | 50% { background-color: transparent; } 83 | 100% {} 84 | } 85 | 86 | /* Can style cursor different in overwrite (non-insert) mode */ 87 | .CodeMirror-overwrite .CodeMirror-cursor {} 88 | 89 | .cm-tab { display: inline-block; text-decoration: inherit; } 90 | 91 | .CodeMirror-rulers { 92 | position: absolute; 93 | left: 0; right: 0; top: -50px; bottom: -20px; 94 | overflow: hidden; 95 | } 96 | .CodeMirror-ruler { 97 | border-left: 1px solid #ccc; 98 | top: 0; bottom: 0; 99 | position: absolute; 100 | } 101 | 102 | /* DEFAULT THEME */ 103 | 104 | .cm-s-default .cm-header {color: blue;} 105 | .cm-s-default .cm-quote {color: #090;} 106 | .cm-negative {color: #d44;} 107 | .cm-positive {color: #292;} 108 | .cm-header, .cm-strong {font-weight: bold;} 109 | .cm-em {font-style: italic;} 110 | .cm-link {text-decoration: underline;} 111 | .cm-strikethrough {text-decoration: line-through;} 112 | 113 | .cm-s-default .cm-keyword {color: #708;} 114 | .cm-s-default .cm-atom {color: #219;} 115 | .cm-s-default .cm-number {color: #164;} 116 | .cm-s-default .cm-def {color: #00f;} 117 | .cm-s-default .cm-variable, 118 | .cm-s-default .cm-punctuation, 119 | .cm-s-default .cm-property, 120 | .cm-s-default .cm-operator {} 121 | .cm-s-default .cm-variable-2 {color: #05a;} 122 | .cm-s-default .cm-variable-3 {color: #085;} 123 | .cm-s-default .cm-comment {color: #a50;} 124 | .cm-s-default .cm-string {color: #a11;} 125 | .cm-s-default .cm-string-2 {color: #f50;} 126 | .cm-s-default .cm-meta {color: #555;} 127 | .cm-s-default .cm-qualifier {color: #555;} 128 | .cm-s-default .cm-builtin {color: #30a;} 129 | .cm-s-default .cm-bracket {color: #997;} 130 | .cm-s-default .cm-tag {color: #170;} 131 | .cm-s-default .cm-attribute {color: #00c;} 132 | .cm-s-default .cm-hr {color: #999;} 133 | .cm-s-default .cm-link {color: #00c;} 134 | 135 | .cm-s-default .cm-error {color: #f00;} 136 | .cm-invalidchar {color: #f00;} 137 | 138 | .CodeMirror-composing { border-bottom: 2px solid; } 139 | 140 | /* Default styles for common addons */ 141 | 142 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 143 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 144 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 145 | .CodeMirror-activeline-background {background: #e8f2ff;} 146 | 147 | /* STOP */ 148 | 149 | /* The rest of this file contains styles related to the mechanics of 150 | the editor. You probably shouldn't touch them. */ 151 | 152 | .CodeMirror { 153 | position: relative; 154 | overflow: hidden; 155 | background: white; 156 | } 157 | 158 | .CodeMirror-scroll { 159 | overflow: scroll !important; /* Things will break if this is overridden */ 160 | /* 30px is the magic margin used to hide the element's real scrollbars */ 161 | /* See overflow: hidden in .CodeMirror */ 162 | margin-bottom: -30px; margin-right: -30px; 163 | padding-bottom: 30px; 164 | height: 100%; 165 | outline: none; /* Prevent dragging from highlighting the element */ 166 | position: relative; 167 | } 168 | .CodeMirror-sizer { 169 | position: relative; 170 | border-right: 30px solid transparent; 171 | } 172 | 173 | /* The fake, visible scrollbars. Used to force redraw during scrolling 174 | before actual scrolling happens, thus preventing shaking and 175 | flickering artifacts. */ 176 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 177 | position: absolute; 178 | z-index: 6; 179 | display: none; 180 | } 181 | .CodeMirror-vscrollbar { 182 | right: 0; top: 0; 183 | overflow-x: hidden; 184 | overflow-y: scroll; 185 | } 186 | .CodeMirror-hscrollbar { 187 | bottom: 0; left: 0; 188 | overflow-y: hidden; 189 | overflow-x: scroll; 190 | } 191 | .CodeMirror-scrollbar-filler { 192 | right: 0; bottom: 0; 193 | } 194 | .CodeMirror-gutter-filler { 195 | left: 0; bottom: 0; 196 | } 197 | 198 | .CodeMirror-gutters { 199 | position: absolute; left: 0; top: 0; 200 | min-height: 100%; 201 | z-index: 3; 202 | } 203 | .CodeMirror-gutter { 204 | white-space: normal; 205 | height: 100%; 206 | display: inline-block; 207 | vertical-align: top; 208 | margin-bottom: -30px; 209 | } 210 | .CodeMirror-gutter-wrapper { 211 | position: absolute; 212 | z-index: 4; 213 | background: none !important; 214 | border: none !important; 215 | } 216 | .CodeMirror-gutter-background { 217 | position: absolute; 218 | top: 0; bottom: 0; 219 | z-index: 4; 220 | } 221 | .CodeMirror-gutter-elt { 222 | position: absolute; 223 | cursor: default; 224 | z-index: 4; 225 | } 226 | .CodeMirror-gutter-wrapper { 227 | -webkit-user-select: none; 228 | -moz-user-select: none; 229 | user-select: none; 230 | } 231 | 232 | .CodeMirror-lines { 233 | cursor: text; 234 | min-height: 1px; /* prevents collapsing before first draw */ 235 | } 236 | .CodeMirror pre { 237 | /* Reset some styles that the rest of the page might have set */ 238 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 239 | border-width: 0; 240 | background: transparent; 241 | font-family: inherit; 242 | font-size: inherit; 243 | margin: 0; 244 | white-space: pre; 245 | word-wrap: normal; 246 | line-height: inherit; 247 | color: inherit; 248 | z-index: 2; 249 | position: relative; 250 | overflow: visible; 251 | -webkit-tap-highlight-color: transparent; 252 | -webkit-font-variant-ligatures: none; 253 | font-variant-ligatures: none; 254 | } 255 | .CodeMirror-wrap pre { 256 | word-wrap: break-word; 257 | white-space: pre-wrap; 258 | word-break: normal; 259 | } 260 | 261 | .CodeMirror-linebackground { 262 | position: absolute; 263 | left: 0; right: 0; top: 0; bottom: 0; 264 | z-index: 0; 265 | } 266 | 267 | .CodeMirror-linewidget { 268 | position: relative; 269 | z-index: 2; 270 | overflow: auto; 271 | } 272 | 273 | .CodeMirror-widget {} 274 | 275 | .CodeMirror-code { 276 | outline: none; 277 | } 278 | 279 | /* Force content-box sizing for the elements where we expect it */ 280 | .CodeMirror-scroll, 281 | .CodeMirror-sizer, 282 | .CodeMirror-gutter, 283 | .CodeMirror-gutters, 284 | .CodeMirror-linenumber { 285 | -moz-box-sizing: content-box; 286 | box-sizing: content-box; 287 | } 288 | 289 | .CodeMirror-measure { 290 | position: absolute; 291 | width: 100%; 292 | height: 0; 293 | overflow: hidden; 294 | visibility: hidden; 295 | } 296 | 297 | .CodeMirror-cursor { 298 | position: absolute; 299 | pointer-events: none; 300 | } 301 | .CodeMirror-measure pre { position: static; } 302 | 303 | div.CodeMirror-cursors { 304 | visibility: hidden; 305 | position: relative; 306 | z-index: 3; 307 | } 308 | div.CodeMirror-dragcursors { 309 | visibility: visible; 310 | } 311 | 312 | .CodeMirror-focused div.CodeMirror-cursors { 313 | visibility: visible; 314 | } 315 | 316 | .CodeMirror-selected { background: #d9d9d9; } 317 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 318 | .CodeMirror-crosshair { cursor: crosshair; } 319 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } 320 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } 321 | 322 | .cm-searching { 323 | background: #ffa; 324 | background: rgba(255, 255, 0, .4); 325 | } 326 | 327 | /* Used to force a border model for a node */ 328 | .cm-force-border { padding-right: .1px; } 329 | 330 | @media print { 331 | /* Hide the cursor when printing */ 332 | .CodeMirror div.CodeMirror-cursors { 333 | visibility: hidden; 334 | } 335 | } 336 | 337 | /* See issue #2901 */ 338 | .cm-tab-wrap-hack:after { content: ''; } 339 | 340 | /* Help users use markselection to safely style text background */ 341 | span.CodeMirror-selectedtext { background: none; } 342 | -------------------------------------------------------------------------------- /src/css/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: 300px; 7 | color: black; 8 | } 9 | 10 | /* PADDING */ 11 | 12 | .CodeMirror-lines { 13 | padding: 4px 0; /* Vertical padding around content */ 14 | } 15 | .CodeMirror pre { 16 | padding: 0 4px; /* Horizontal padding of content */ 17 | } 18 | 19 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 20 | background-color: white; /* The little square between H and V scrollbars */ 21 | } 22 | 23 | /* GUTTER */ 24 | 25 | .CodeMirror-gutters { 26 | border-right: 1px solid #ddd; 27 | background-color: #f7f7f7; 28 | white-space: nowrap; 29 | } 30 | .CodeMirror-linenumbers {} 31 | .CodeMirror-linenumber { 32 | padding: 0 3px 0 5px; 33 | min-width: 20px; 34 | text-align: right; 35 | color: #999; 36 | white-space: nowrap; 37 | } 38 | 39 | .CodeMirror-guttermarker { color: black; } 40 | .CodeMirror-guttermarker-subtle { color: #999; } 41 | 42 | /* CURSOR */ 43 | 44 | .CodeMirror-cursor { 45 | border-left: 1px solid black; 46 | border-right: none; 47 | width: 0; 48 | } 49 | /* Shown when moving in bi-directional text */ 50 | .CodeMirror div.CodeMirror-secondarycursor { 51 | border-left: 1px solid silver; 52 | } 53 | .cm-fat-cursor .CodeMirror-cursor { 54 | width: auto; 55 | border: 0 !important; 56 | background: #7e7; 57 | } 58 | .cm-fat-cursor div.CodeMirror-cursors { 59 | z-index: 1; 60 | } 61 | 62 | .cm-animate-fat-cursor { 63 | width: auto; 64 | border: 0; 65 | -webkit-animation: blink 1.06s steps(1) infinite; 66 | -moz-animation: blink 1.06s steps(1) infinite; 67 | animation: blink 1.06s steps(1) infinite; 68 | background-color: #7e7; 69 | } 70 | @-moz-keyframes blink { 71 | 0% {} 72 | 50% { background-color: transparent; } 73 | 100% {} 74 | } 75 | @-webkit-keyframes blink { 76 | 0% {} 77 | 50% { background-color: transparent; } 78 | 100% {} 79 | } 80 | @keyframes blink { 81 | 0% {} 82 | 50% { background-color: transparent; } 83 | 100% {} 84 | } 85 | 86 | /* Can style cursor different in overwrite (non-insert) mode */ 87 | .CodeMirror-overwrite .CodeMirror-cursor {} 88 | 89 | .cm-tab { display: inline-block; text-decoration: inherit; } 90 | 91 | .CodeMirror-rulers { 92 | position: absolute; 93 | left: 0; right: 0; top: -50px; bottom: -20px; 94 | overflow: hidden; 95 | } 96 | .CodeMirror-ruler { 97 | border-left: 1px solid #ccc; 98 | top: 0; bottom: 0; 99 | position: absolute; 100 | } 101 | 102 | /* DEFAULT THEME */ 103 | 104 | .cm-s-default .cm-header {color: blue;} 105 | .cm-s-default .cm-quote {color: #090;} 106 | .cm-negative {color: #d44;} 107 | .cm-positive {color: #292;} 108 | .cm-header, .cm-strong {font-weight: bold;} 109 | .cm-em {font-style: italic;} 110 | .cm-link {text-decoration: underline;} 111 | .cm-strikethrough {text-decoration: line-through;} 112 | 113 | .cm-s-default .cm-keyword {color: #708;} 114 | .cm-s-default .cm-atom {color: #219;} 115 | .cm-s-default .cm-number {color: #164;} 116 | .cm-s-default .cm-def {color: #00f;} 117 | .cm-s-default .cm-variable, 118 | .cm-s-default .cm-punctuation, 119 | .cm-s-default .cm-property, 120 | .cm-s-default .cm-operator {} 121 | .cm-s-default .cm-variable-2 {color: #05a;} 122 | .cm-s-default .cm-variable-3 {color: #085;} 123 | .cm-s-default .cm-comment {color: #a50;} 124 | .cm-s-default .cm-string {color: #a11;} 125 | .cm-s-default .cm-string-2 {color: #f50;} 126 | .cm-s-default .cm-meta {color: #555;} 127 | .cm-s-default .cm-qualifier {color: #555;} 128 | .cm-s-default .cm-builtin {color: #30a;} 129 | .cm-s-default .cm-bracket {color: #997;} 130 | .cm-s-default .cm-tag {color: #170;} 131 | .cm-s-default .cm-attribute {color: #00c;} 132 | .cm-s-default .cm-hr {color: #999;} 133 | .cm-s-default .cm-link {color: #00c;} 134 | 135 | .cm-s-default .cm-error {color: #f00;} 136 | .cm-invalidchar {color: #f00;} 137 | 138 | .CodeMirror-composing { border-bottom: 2px solid; } 139 | 140 | /* Default styles for common addons */ 141 | 142 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 143 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 144 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } 145 | .CodeMirror-activeline-background {background: #e8f2ff;} 146 | 147 | /* STOP */ 148 | 149 | /* The rest of this file contains styles related to the mechanics of 150 | the editor. You probably shouldn't touch them. */ 151 | 152 | .CodeMirror { 153 | position: relative; 154 | overflow: hidden; 155 | background: white; 156 | } 157 | 158 | .CodeMirror-scroll { 159 | overflow: scroll !important; /* Things will break if this is overridden */ 160 | /* 30px is the magic margin used to hide the element's real scrollbars */ 161 | /* See overflow: hidden in .CodeMirror */ 162 | margin-bottom: -30px; margin-right: -30px; 163 | padding-bottom: 30px; 164 | height: 100%; 165 | outline: none; /* Prevent dragging from highlighting the element */ 166 | position: relative; 167 | } 168 | .CodeMirror-sizer { 169 | position: relative; 170 | border-right: 30px solid transparent; 171 | } 172 | 173 | /* The fake, visible scrollbars. Used to force redraw during scrolling 174 | before actual scrolling happens, thus preventing shaking and 175 | flickering artifacts. */ 176 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 177 | position: absolute; 178 | z-index: 6; 179 | display: none; 180 | } 181 | .CodeMirror-vscrollbar { 182 | right: 0; top: 0; 183 | overflow-x: hidden; 184 | overflow-y: scroll; 185 | } 186 | .CodeMirror-hscrollbar { 187 | bottom: 0; left: 0; 188 | overflow-y: hidden; 189 | overflow-x: scroll; 190 | } 191 | .CodeMirror-scrollbar-filler { 192 | right: 0; bottom: 0; 193 | } 194 | .CodeMirror-gutter-filler { 195 | left: 0; bottom: 0; 196 | } 197 | 198 | .CodeMirror-gutters { 199 | position: absolute; left: 0; top: 0; 200 | min-height: 100%; 201 | z-index: 3; 202 | } 203 | .CodeMirror-gutter { 204 | white-space: normal; 205 | height: 100%; 206 | display: inline-block; 207 | vertical-align: top; 208 | margin-bottom: -30px; 209 | } 210 | .CodeMirror-gutter-wrapper { 211 | position: absolute; 212 | z-index: 4; 213 | background: none !important; 214 | border: none !important; 215 | } 216 | .CodeMirror-gutter-background { 217 | position: absolute; 218 | top: 0; bottom: 0; 219 | z-index: 4; 220 | } 221 | .CodeMirror-gutter-elt { 222 | position: absolute; 223 | cursor: default; 224 | z-index: 4; 225 | } 226 | .CodeMirror-gutter-wrapper { 227 | -webkit-user-select: none; 228 | -moz-user-select: none; 229 | user-select: none; 230 | } 231 | 232 | .CodeMirror-lines { 233 | cursor: text; 234 | min-height: 1px; /* prevents collapsing before first draw */ 235 | } 236 | .CodeMirror pre { 237 | /* Reset some styles that the rest of the page might have set */ 238 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 239 | border-width: 0; 240 | background: transparent; 241 | font-family: inherit; 242 | font-size: inherit; 243 | margin: 0; 244 | white-space: pre; 245 | word-wrap: normal; 246 | line-height: inherit; 247 | color: inherit; 248 | z-index: 2; 249 | position: relative; 250 | overflow: visible; 251 | -webkit-tap-highlight-color: transparent; 252 | -webkit-font-variant-ligatures: none; 253 | font-variant-ligatures: none; 254 | } 255 | .CodeMirror-wrap pre { 256 | word-wrap: break-word; 257 | white-space: pre-wrap; 258 | word-break: normal; 259 | } 260 | 261 | .CodeMirror-linebackground { 262 | position: absolute; 263 | left: 0; right: 0; top: 0; bottom: 0; 264 | z-index: 0; 265 | } 266 | 267 | .CodeMirror-linewidget { 268 | position: relative; 269 | z-index: 2; 270 | overflow: auto; 271 | } 272 | 273 | .CodeMirror-widget {} 274 | 275 | .CodeMirror-code { 276 | outline: none; 277 | } 278 | 279 | /* Force content-box sizing for the elements where we expect it */ 280 | .CodeMirror-scroll, 281 | .CodeMirror-sizer, 282 | .CodeMirror-gutter, 283 | .CodeMirror-gutters, 284 | .CodeMirror-linenumber { 285 | -moz-box-sizing: content-box; 286 | box-sizing: content-box; 287 | } 288 | 289 | .CodeMirror-measure { 290 | position: absolute; 291 | width: 100%; 292 | height: 0; 293 | overflow: hidden; 294 | visibility: hidden; 295 | } 296 | 297 | .CodeMirror-cursor { 298 | position: absolute; 299 | pointer-events: none; 300 | } 301 | .CodeMirror-measure pre { position: static; } 302 | 303 | div.CodeMirror-cursors { 304 | visibility: hidden; 305 | position: relative; 306 | z-index: 3; 307 | } 308 | div.CodeMirror-dragcursors { 309 | visibility: visible; 310 | } 311 | 312 | .CodeMirror-focused div.CodeMirror-cursors { 313 | visibility: visible; 314 | } 315 | 316 | .CodeMirror-selected { background: #d9d9d9; } 317 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 318 | .CodeMirror-crosshair { cursor: crosshair; } 319 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } 320 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } 321 | 322 | .cm-searching { 323 | background: #ffa; 324 | background: rgba(255, 255, 0, .4); 325 | } 326 | 327 | /* Used to force a border model for a node */ 328 | .cm-force-border { padding-right: .1px; } 329 | 330 | @media print { 331 | /* Hide the cursor when printing */ 332 | .CodeMirror div.CodeMirror-cursors { 333 | visibility: hidden; 334 | } 335 | } 336 | 337 | /* See issue #2901 */ 338 | .cm-tab-wrap-hack:after { content: ''; } 339 | 340 | /* Help users use markselection to safely style text background */ 341 | span.CodeMirror-selectedtext { background: none; } 342 | -------------------------------------------------------------------------------- /src/refmt/refmt2.re: -------------------------------------------------------------------------------- 1 | open RefmtShared; 2 | 3 | [@bs.module "../../../../src/refmt/refmt.js"] external parseREEx : string => ast = "parseRE"; 4 | 5 | [@bs.module "../../../../src/refmt/refmt.js"] external parseREIEx : string => ast = "parseREI"; 6 | 7 | [@bs.module "../../../../src/refmt/refmt.js"] external parseMLEx : string => ast = "parseML"; 8 | 9 | [@bs.module "../../../../src/refmt/refmt.js"] external parseMLIEx : string => ast = "parseMLI"; 10 | 11 | [@bs.module "../../../../src/refmt/refmt.js"] external printRE : ast => string = ""; 12 | 13 | [@bs.module "../../../../src/refmt/refmt.js"] external printREI : ast => string = ""; 14 | 15 | [@bs.module "../../../../src/refmt/refmt.js"] external printML : ast => string = ""; 16 | 17 | [@bs.module "../../../../src/refmt/refmt.js"] external printMLI : ast => string = ""; 18 | 19 | [@bs.module "../../../../src/refmt/refmtOld.js"] external parseREOldEx : string => ast = "parseRE"; 20 | 21 | [@bs.module "../../../../src/refmt/refmtOld.js"] 22 | external parseREIOldEx : string => ast = "parseREI"; 23 | 24 | [@bs.module "../../../../src/refmt/refmtOld.js"] external printREOld : ast => string = "printRE"; 25 | 26 | [@bs.module "../../../../src/refmt/refmtOld.js"] external printREIOld : ast => string = "printREI"; 27 | 28 | let parseRE = (str) => 29 | try (Ast(RE(parseREEx(str)))) { 30 | | Js.Exn.Error(parseError) => Error(RE(parseError)) 31 | }; 32 | 33 | let parseREI = (str) => 34 | try (Ast(REI(parseREIEx(str)))) { 35 | | Js.Exn.Error(parseError) => Error(REI(parseError)) 36 | }; 37 | 38 | let parseML = (str) => 39 | try (Ast(ML(parseMLEx(str)))) { 40 | | Js.Exn.Error(parseError) => Error(ML(parseError)) 41 | }; 42 | 43 | let parseMLI = (str) => 44 | try (Ast(MLI(parseMLIEx(str)))) { 45 | | Js.Exn.Error(parseError) => Error(MLI(parseError)) 46 | }; /* let printRE (Ast (RE ast)) => ( (printREEx ast)); 47 | let printREI (Ast (REI ast)) => ( (printREIEx ast )); 48 | let printML (Ast (ML ast)) => ( (printMLEx ast)); 49 | let printMLI (Ast (MLI ast)) => ( (printMLIEx ast )); */ 50 | 51 | let parseREOld = (str) => 52 | try (Ast(REO(parseREOldEx(str)))) { 53 | | Js.Exn.Error(parseError) => Error(REO(parseError)) 54 | }; 55 | 56 | let parseREIOld = (str) => 57 | try (Ast(REOI(parseREIOldEx(str)))) { 58 | | Js.Exn.Error(parseError) => Error(REOI(parseError)) 59 | }; 60 | 61 | let (|?) = (a, b) => 62 | switch a { 63 | | Some(a) => a 64 | | None => b 65 | } /* let (>!) (loc1: Location.t) (loc2: Location.t) => { 66 | let (_, line1, col1) = Location.get_pos_info loc1.loc_start; 67 | let (_, line2, col2) = Location.get_pos_info loc2.loc_start; 68 | line1 === line2 ? col1 > col2 : line1 > line2 69 | }; */ /* let string_of_code printer code => { 70 | ignore (Format.flush_str_formatter ()); 71 | let f = Format.str_formatter; 72 | printer f code; 73 | Format.flush_str_formatter () 74 | }; */ /* let parseRE code => 75 | Ast (RE (Reason_toolchain.JS.canonical_implementation_with_comments (Lexing.from_string code))); 76 | 77 | let parseML code => 78 | Ast (ML (Reason_toolchain.ML.canonical_implementation_with_comments (Lexing.from_string code))); 79 | 80 | let parseREI code => 81 | Ast (REI (Reason_toolchain.JS.canonical_interface_with_comments (Lexing.from_string code))); 82 | 83 | let parseMLI code => 84 | Ast (MLI (Reason_toolchain.ML.canonical_interface_with_comments (Lexing.from_string code))); 85 | 86 | let printML implementation => 87 | string_of_code Reason_toolchain.ML.print_canonical_implementation_with_comments implementation; 88 | 89 | let printMLI signature => 90 | string_of_code Reason_toolchain.ML.print_canonical_interface_with_comments signature; 91 | 92 | let printRE = string_of_code Reason_toolchain.JS.print_canonical_implementation_with_comments; 93 | 94 | let printREI = string_of_code Reason_toolchain.JS.print_canonical_interface_with_comments; */ /* |> */; /* let printREOld (Ast (REO ast)) => ( (printREOldEx ast )); 95 | let printREIOld (Ast (REOI ast)) => ( (printREIOldEx ast)); */ 96 | 97 | let genErrors = (errors) => 98 | switch errors { 99 | | [] => (UnknownLang, UnknownLang, "Sorry we don't have much info. Something went very wrong.") 100 | | [error, ..._] => 101 | let thing = 102 | switch error { 103 | | REI(thing) => Js.Exn.message(thing) 104 | | RE(thing) => Js.Exn.message(thing) 105 | | ML(thing) => Js.Exn.message(thing) 106 | | MLI(thing) => Js.Exn.message(thing) 107 | | REO(thing) => Js.Exn.message(thing) 108 | | REOI(thing) => Js.Exn.message(thing) 109 | }; 110 | ( 111 | UnknownLang, 112 | UnknownLang, 113 | switch thing { 114 | | Some(str) => str 115 | | None => "Sorry we don't have much info. Something went very wrong." 116 | } 117 | ) /* switch (Js.Json.stringifyAny error) { 118 | | Some error => error 119 | | None => "Sorry we don't have much info. Something went very wrong." 120 | }) */ /* let error = 121 | List.fold_left 122 | ( 123 | fun ((prevLoc, _, _) as prev) error => { 124 | let (optionLoc, _, _) as curr = 125 | switch error { 126 | | Syntaxerr.Error error => 127 | /* ML */ 128 | let loc = Syntaxerr.location_of_error error; 129 | let error = string_of_code Syntaxerr.report_error error; 130 | (Some loc, error, ML) 131 | | Syntax_util.Error loc error => 132 | /* RE */ 133 | let error = string_of_code Syntax_util.report_error error; 134 | (Some loc, error, RE) 135 | | finalExn => (None, Printexc.to_string finalExn, UnknownLang) 136 | }; 137 | switch (optionLoc, prevLoc) { 138 | | (Some loc, Some prevLoc) => loc >! prevLoc ? curr : prev 139 | | (Some _, None) => curr 140 | | _ => prev 141 | } 142 | } 143 | ) 144 | (None, "", UnknownLang) 145 | errors; 146 | switch error { 147 | | (None, message, inLang) => (inLang, UnknownLang, message) 148 | /* Reason error message printer doesn't include location, ML does */ 149 | | (Some loc, message, inLang) when inLang == RE => ( 150 | inLang, 151 | UnknownLang, 152 | string_of_code Location.print_loc loc ^ ":\n" ^ message 153 | ) 154 | | (_, message, inLang) => (inLang, UnknownLang, message) 155 | } */ 156 | }; 157 | 158 | let attempts = (inLang, inType) => 159 | switch (inLang, inType) { 160 | | (ML, Implementation) => [parseML] 161 | | (ML, Interface) => [parseMLI] 162 | | (RE, Implementation) => [parseRE] 163 | | (RE, Interface) => [parseREI] 164 | | (REO, Implementation) => [parseREI] 165 | | (REO, Interface) => [parseREI] 166 | | (UnknownLang, Implementation) => [parseRE, parseREOld, parseML] 167 | | (UnknownLang, Interface) => [parseREI, parseREIOld, parseMLI] 168 | | (ML, UnknownType) => [parseML, parseMLI] 169 | | (RE, UnknownType) => [parseRE, parseREI] 170 | | (REO, UnknownType) => [parseREOld, parseREIOld] 171 | | (UnknownLang, UnknownType) => [parseRE, parseREI, parseREOld, parseREIOld, parseML, parseMLI] 172 | }; 173 | 174 | let optionalString = fun | Some(s:string) => s | None => ""; 175 | 176 | let refmt = (code, inLang, inType, outLang) => { 177 | let parsersToTry = attempts(inLang, inType); 178 | let results = List.map((parser) => parser(code), parsersToTry); 179 | try { 180 | let resultList = 181 | List.find_all( 182 | (res) => 183 | switch res { 184 | | Error(_) => false 185 | | _ => true 186 | }, 187 | results 188 | ); 189 | let result = 190 | switch (resultList, results) { 191 | | ([], [error]) 192 | | ([], [error, ..._]) => error 193 | | ([result], _) 194 | | ([result, ..._], _) => result 195 | | (_, _) => assert false 196 | }; 197 | let (trueOut, printedResult) = 198 | switch (outLang, result) { 199 | | (RE, Ast(ML(ast))) 200 | | (RE, Ast(RE(ast))) 201 | | (RE, Ast(REO(ast))) 202 | | (UnknownLang, Ast(REO(ast))) 203 | | (UnknownLang, Ast(ML(ast))) => (RE, printRE(ast)) 204 | | (REO, Ast(ML(ast))) 205 | | (REO, Ast(RE(ast))) 206 | | (REO, Ast(REO(ast))) => (REO, printREOld(ast)) 207 | | (ML, Ast(ML(ast))) 208 | | (ML, Ast(RE(ast))) 209 | | (ML, Ast(REO(ast))) 210 | | (UnknownLang, Ast(RE(ast))) => (ML, printML(ast)) 211 | | (RE, Ast(MLI(ast))) 212 | | (RE, Ast(REI(ast))) 213 | | (RE, Ast(REOI(ast))) 214 | | (UnknownLang, Ast(REOI(ast))) 215 | | (UnknownLang, Ast(MLI(ast))) => (RE, printREI(ast)) 216 | | (REO, Ast(MLI(ast))) 217 | | (REO, Ast(REI(ast))) 218 | | (REO, Ast(REOI(ast))) => (REO, printREIOld(ast)) 219 | | (ML, Ast(MLI(ast))) 220 | | (ML, Ast(REI(ast))) 221 | | (ML, Ast(REOI(ast))) 222 | | (UnknownLang, Ast(REI(ast))) => (ML, printMLI(ast)) 223 | | (_, Error(error)) => 224 | switch error { 225 | | REI(thing) => raise(Failure(Js.Exn.message(thing)|>optionalString)) 226 | | RE(thing) => raise(Failure(Js.Exn.message(thing)|>optionalString)) 227 | | ML(thing) => raise(Failure(Js.Exn.message(thing)|>optionalString)) 228 | | MLI(thing) => raise(Failure(Js.Exn.message(thing)|>optionalString)) 229 | | REO(thing) => raise(Failure(Js.Exn.message(thing)|>optionalString)) 230 | | REOI(thing) => raise(Failure(Js.Exn.message(thing)|>optionalString)) 231 | } 232 | }; 233 | let trueIn = 234 | switch result { 235 | | Ast(ML(_)) 236 | | Ast(MLI(_)) => ML 237 | | Ast(RE(_)) 238 | | Ast(REI(_)) => RE 239 | | Ast(REO(_)) 240 | | Ast(REOI(_)) => REO 241 | | Error(_) => UnknownLang 242 | }; 243 | Result.Ok((trueIn, trueOut, printedResult)) 244 | } { 245 | | a => { 246 | Result.Error(a |> Printexc.to_string) 247 | } 248 | } 249 | }; 250 | -------------------------------------------------------------------------------- /docs/ocamlDoc.css: -------------------------------------------------------------------------------- 1 | /** 2 | * This CSS file for documentation generation embeds Normalize.css 3 | * https://necolas.github.io/normalize.css/4.1.1/normalize.css 4 | * 5 | * And then specifies a bunch of custom styles for the ocamldoc generation: 6 | * 7 | */ 8 | 9 | 10 | 11 | 12 | /* bodyFontSize */ 13 | font-size: 14px; 14 | 15 | /* codeFontSize */ 16 | font-size: 13px 17 | 18 | 19 | /* infoBackgroundColor */ 20 | background-color: #30302d; 21 | 22 | /* linkColor */ 23 | color: #cec47f; 24 | /* linkColorActive */ 25 | color: #faf0ab; 26 | 27 | 28 | 29 | /** 30 | * Space Gray Dark: 31 | * ================ 32 | */ 33 | /* backgroundColorDark */ 34 | background-color: #262b36; 35 | 36 | /* backgroundColor */ 37 | background-color: #2b303b; 38 | 39 | /* backgroundColorPanel */ 40 | background-color: #313641; 41 | 42 | /* borderLeftColorPanel */ 43 | border-left: 4px solid rgba(101,115,126, 0.2); 44 | 45 | /* borderColorInlineCode */ 46 | border: 1px solid #3b3f4a; 47 | 48 | /* backgroundColorInlineCode */ 49 | background-color: #393d48; 50 | 51 | /* backgroundColorInlineBorder */ 52 | border: 1px solid #474b56; 53 | 54 | /* colorText */ 55 | color: #c0c5ce; 56 | 57 | /* primaryColor */ 58 | color: #8fa1b3; 59 | 60 | /* text */ 61 | /* colorFadedText (comment) */ 62 | 5px solid rgba(101,115,126,1); 63 | 64 | /* let */ 65 | /* colorKeyword (purple) */ 66 | color: #b48ead; 67 | 68 | /* colorRed */ 69 | color: #BF616A; 70 | /* colorRedActive */ 71 | color: #CB6D77; 72 | 73 | /* colorGreen */ 74 | color: #91B376; 75 | 76 | /* colorGreenActive */ 77 | color: #97B98c; 78 | 79 | /* colorYellow */ 80 | color: #EBCB8B; 81 | 82 | /* colorYellowActive */ 83 | color: #FBDB9B; 84 | 85 | /* colorCyan */ 86 | color: #96B5B4; 87 | 88 | 89 | 90 | /* 91 | 92 | 93 | TopInfoBg: 94 | #424240 95 | 96 | TopInfoCodeBg: 97 | #545454 98 | 99 | 100 | InfoBgLeftBorder: 101 | #666664 102 | 103 | KeyWord (let type etc): 104 | #ecac9d 105 | 106 | Type: 107 | #ff0001; 108 | 109 | Comment: 110 | #aeaea1; 111 | 112 | 113 | .navbar > a:nth-child(1n) { 114 | border-bottom: 4px solid #d7df9d; 115 | } 116 | 117 | .navbar > a:nth-child(2n) { 118 | border-bottom: 4px solid #ef95a4; 119 | } 120 | 121 | .navbar > a:nth-child(3n) { 122 | border-bottom: 4px solid #aabbd0; 123 | } 124 | 125 | .navbar > a:nth-child(4n) { 126 | border-bottom: 4px solid #cec47f; 127 | } 128 | 129 | .navbar > a:nth-child(5n) { 130 | border-bottom: 4px solid #b58900; 131 | } 132 | 133 | 134 | /*! normalize.css v4.1.1 | MIT License | github.com/necolas/normalize.css */ 135 | 136 | /** 137 | * 1. Change the default font family in all browsers (opinionated). 138 | * 2. Prevent adjustments of font size after orientation changes in IE and iOS. 139 | */ 140 | 141 | html { 142 | font-family: sans-serif; /* 1 */ 143 | -ms-text-size-adjust: 100%; /* 2 */ 144 | -webkit-text-size-adjust: 100%; /* 2 */ 145 | } 146 | 147 | /** 148 | * Remove the margin in all browsers (opinionated). 149 | */ 150 | 151 | body { 152 | margin: 0; 153 | } 154 | 155 | /* HTML5 display definitions 156 | ========================================================================== */ 157 | 158 | /** 159 | * Add the correct display in IE 9-. 160 | * 1. Add the correct display in Edge, IE, and Firefox. 161 | * 2. Add the correct display in IE. 162 | */ 163 | 164 | article, 165 | aside, 166 | details, /* 1 */ 167 | figcaption, 168 | figure, 169 | footer, 170 | header, 171 | main, /* 2 */ 172 | menu, 173 | nav, 174 | section, 175 | summary { /* 1 */ 176 | display: block; 177 | } 178 | 179 | /** 180 | * Add the correct display in IE 9-. 181 | */ 182 | 183 | audio, 184 | canvas, 185 | progress, 186 | video { 187 | display: inline-block; 188 | } 189 | 190 | /** 191 | * Add the correct display in iOS 4-7. 192 | */ 193 | 194 | audio:not([controls]) { 195 | display: none; 196 | height: 0; 197 | } 198 | 199 | /** 200 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 201 | */ 202 | 203 | progress { 204 | vertical-align: baseline; 205 | } 206 | 207 | /** 208 | * Add the correct display in IE 10-. 209 | * 1. Add the correct display in IE. 210 | */ 211 | 212 | template, /* 1 */ 213 | [hidden] { 214 | display: none; 215 | } 216 | 217 | /* Links 218 | ========================================================================== */ 219 | 220 | /** 221 | * 1. Remove the gray background on active links in IE 10. 222 | * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. 223 | */ 224 | 225 | a { 226 | background-color: transparent; /* 1 */ 227 | -webkit-text-decoration-skip: objects; /* 2 */ 228 | } 229 | 230 | /** 231 | * Remove the outline on focused links when they are also active or hovered 232 | * in all browsers (opinionated). 233 | */ 234 | 235 | a:active, 236 | a:hover { 237 | outline-width: 0; 238 | } 239 | 240 | /* Text-level semantics 241 | ========================================================================== */ 242 | 243 | /** 244 | * 1. Remove the bottom border in Firefox 39-. 245 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 246 | */ 247 | 248 | abbr[title] { 249 | border-bottom: none; /* 1 */ 250 | text-decoration: underline; /* 2 */ 251 | text-decoration: underline dotted; /* 2 */ 252 | } 253 | 254 | /** 255 | * Prevent the duplicate application of `bolder` by the next rule in Safari 6. 256 | */ 257 | 258 | b, 259 | strong { 260 | font-weight: inherit; 261 | } 262 | 263 | /** 264 | * Add the correct font weight in Chrome, Edge, and Safari. 265 | */ 266 | 267 | b, 268 | strong { 269 | font-weight: bolder; 270 | } 271 | 272 | /** 273 | * Add the correct font style in Android 4.3-. 274 | */ 275 | 276 | dfn { 277 | font-style: italic; 278 | } 279 | 280 | /** 281 | * Correct the font size and margin on `h1` elements within `section` and 282 | * `article` contexts in Chrome, Firefox, and Safari. 283 | */ 284 | 285 | h1 { 286 | font-size: 2em; 287 | margin: 0.67em 0; 288 | } 289 | 290 | /** 291 | * Add the correct background and color in IE 9-. 292 | */ 293 | 294 | mark { 295 | background-color: #ff0; 296 | color: #000; 297 | } 298 | 299 | /** 300 | * Add the correct font size in all browsers. 301 | */ 302 | 303 | small { 304 | font-size: 80%; 305 | } 306 | 307 | /** 308 | * Prevent `sub` and `sup` elements from affecting the line height in 309 | * all browsers. 310 | */ 311 | 312 | sub, 313 | sup { 314 | font-size: 75%; 315 | line-height: 0; 316 | position: relative; 317 | vertical-align: baseline; 318 | } 319 | 320 | sub { 321 | bottom: -0.25em; 322 | } 323 | 324 | sup { 325 | top: -0.5em; 326 | } 327 | 328 | /* Embedded content 329 | ========================================================================== */ 330 | 331 | /** 332 | * Remove the border on images inside links in IE 10-. 333 | */ 334 | 335 | img { 336 | border-style: none; 337 | } 338 | 339 | /** 340 | * Hide the overflow in IE. 341 | */ 342 | 343 | svg:not(:root) { 344 | overflow: hidden; 345 | } 346 | 347 | /* Grouping content 348 | ========================================================================== */ 349 | 350 | /** 351 | * 1. Correct the inheritance and scaling of font size in all browsers. 352 | * 2. Correct the odd `em` font sizing in all browsers. 353 | */ 354 | 355 | code, 356 | kbd, 357 | pre, 358 | samp { 359 | font-family: monospace, monospace; /* 1 */ 360 | font-size: 1em; /* 2 */ 361 | } 362 | 363 | /** 364 | * Add the correct margin in IE 8. 365 | */ 366 | 367 | figure { 368 | margin: 1em 40px; 369 | } 370 | 371 | /** 372 | * 1. Add the correct box sizing in Firefox. 373 | * 2. Show the overflow in Edge and IE. 374 | */ 375 | 376 | hr { 377 | box-sizing: content-box; /* 1 */ 378 | height: 0; /* 1 */ 379 | overflow: visible; /* 2 */ 380 | } 381 | 382 | /* Forms 383 | ========================================================================== */ 384 | 385 | /** 386 | * 1. Change font properties to `inherit` in all browsers (opinionated). 387 | * 2. Remove the margin in Firefox and Safari. 388 | */ 389 | 390 | button, 391 | input, 392 | select, 393 | textarea { 394 | font: inherit; /* 1 */ 395 | margin: 0; /* 2 */ 396 | } 397 | 398 | /** 399 | * Restore the font weight unset by the previous rule. 400 | */ 401 | 402 | optgroup { 403 | font-weight: bold; 404 | } 405 | 406 | /** 407 | * Show the overflow in IE. 408 | * 1. Show the overflow in Edge. 409 | */ 410 | 411 | button, 412 | input { /* 1 */ 413 | overflow: visible; 414 | } 415 | 416 | /** 417 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 418 | * 1. Remove the inheritance of text transform in Firefox. 419 | */ 420 | 421 | button, 422 | select { /* 1 */ 423 | text-transform: none; 424 | } 425 | 426 | /** 427 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` 428 | * controls in Android 4. 429 | * 2. Correct the inability to style clickable types in iOS and Safari. 430 | */ 431 | 432 | button, 433 | html [type="button"], /* 1 */ 434 | [type="reset"], 435 | [type="submit"] { 436 | -webkit-appearance: button; /* 2 */ 437 | } 438 | 439 | /** 440 | * Remove the inner border and padding in Firefox. 441 | */ 442 | 443 | button::-moz-focus-inner, 444 | [type="button"]::-moz-focus-inner, 445 | [type="reset"]::-moz-focus-inner, 446 | [type="submit"]::-moz-focus-inner { 447 | border-style: none; 448 | padding: 0; 449 | } 450 | 451 | /** 452 | * Restore the focus styles unset by the previous rule. 453 | */ 454 | 455 | button:-moz-focusring, 456 | [type="button"]:-moz-focusring, 457 | [type="reset"]:-moz-focusring, 458 | [type="submit"]:-moz-focusring { 459 | outline: 1px dotted ButtonText; 460 | } 461 | 462 | /** 463 | * Change the border, margin, and padding in all browsers (opinionated). 464 | */ 465 | 466 | fieldset { 467 | border: 1px solid #c0c0c0; 468 | margin: 0 2px; 469 | padding: 0.35em 0.625em 0.75em; 470 | } 471 | 472 | /** 473 | * 1. Correct the text wrapping in Edge and IE. 474 | * 2. Correct the color inheritance from `fieldset` elements in IE. 475 | * 3. Remove the padding so developers are not caught out when they zero out 476 | * `fieldset` elements in all browsers. 477 | */ 478 | 479 | legend { 480 | box-sizing: border-box; /* 1 */ 481 | color: inherit; /* 2 */ 482 | display: table; /* 1 */ 483 | max-width: 100%; /* 1 */ 484 | padding: 0; /* 3 */ 485 | white-space: normal; /* 1 */ 486 | } 487 | 488 | /** 489 | * Remove the default vertical scrollbar in IE. 490 | */ 491 | 492 | textarea { 493 | overflow: auto; 494 | } 495 | 496 | /** 497 | * 1. Add the correct box sizing in IE 10-. 498 | * 2. Remove the padding in IE 10-. 499 | */ 500 | 501 | [type="checkbox"], 502 | [type="radio"] { 503 | box-sizing: border-box; /* 1 */ 504 | padding: 0; /* 2 */ 505 | } 506 | 507 | /** 508 | * Correct the cursor style of increment and decrement buttons in Chrome. 509 | */ 510 | 511 | [type="number"]::-webkit-inner-spin-button, 512 | [type="number"]::-webkit-outer-spin-button { 513 | height: auto; 514 | } 515 | 516 | /** 517 | * 1. Correct the odd appearance in Chrome and Safari. 518 | * 2. Correct the outline style in Safari. 519 | */ 520 | 521 | [type="search"] { 522 | -webkit-appearance: textfield; /* 1 */ 523 | outline-offset: -2px; /* 2 */ 524 | } 525 | 526 | /** 527 | * Remove the inner padding and cancel buttons in Chrome and Safari on OS X. 528 | */ 529 | 530 | [type="search"]::-webkit-search-cancel-button, 531 | [type="search"]::-webkit-search-decoration { 532 | -webkit-appearance: none; 533 | } 534 | 535 | /** 536 | * Correct the text style of placeholders in Chrome, Edge, and Safari. 537 | */ 538 | 539 | ::-webkit-input-placeholder { 540 | color: inherit; 541 | opacity: 0.54; 542 | } 543 | 544 | /** 545 | * 1. Correct the inability to style clickable types in iOS and Safari. 546 | * 2. Change font properties to `inherit` in Safari. 547 | */ 548 | 549 | ::-webkit-file-upload-button { 550 | -webkit-appearance: button; /* 1 */ 551 | font: inherit; /* 2 */ 552 | } 553 | 554 | 555 | 556 | 557 | /** 558 | * CUSTOM CSS: 559 | */ 560 | 561 | ul,ol { 562 | padding: 0; 563 | margin: 10 0 10px 25px 564 | } 565 | 566 | ul ul, ul ol, ol ol, ol ul { 567 | margin-bottom: 0; 568 | } 569 | 570 | li { 571 | line-height: 20px 572 | } 573 | 574 | * { 575 | box-sizing: border-box; 576 | } 577 | 578 | 579 | body { 580 | font-family: "Helvetica", "Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif; 581 | /* bodyFontSize */ 582 | font-size: 14px; 583 | margin: 0px; 584 | line-height: 20px; 585 | /* backgroundColor */ 586 | background-color: #2b303b; 587 | 588 | /* colorText */ 589 | color: #c0c5ce; 590 | padding: 40px; 591 | padding-top: 50px; 592 | } 593 | 594 | td.module { 595 | font-family: Menlo,Menlo,Consolas,"Courier New",monospace; 596 | /* codeFontSize */ 597 | font-size: 13px 598 | } 599 | 600 | 601 | code, pre { 602 | font-family: Menlo,Menlo,Consolas,"Courier New",monospace; 603 | /* codeFontSize */ 604 | font-size: 13px; 605 | color: #333; 606 | } 607 | 608 | a { 609 | color: inherit; 610 | /* colorYellow */ 611 | text-decoration: none; 612 | -webkit-transition: color 0.1s ease-out; 613 | -moz-transition: color 0.1s ease-out; 614 | -o-transition: color 0.1s ease-out; 615 | transition: color 0.1s ease-out; 616 | } 617 | 618 | a:hover,a:focus { 619 | color: inherit; 620 | /* colorYellowActive */ 621 | text-decoration: none; 622 | outline: none; 623 | -webkit-transition: color 0.1s ease-out; 624 | -moz-transition: color 0.1s ease-out; 625 | -o-transition: color 0.1s ease-out; 626 | transition: color 0.1s ease-out; 627 | } 628 | 629 | .typetable { 630 | /* So that top level pre's margin-bottom isn't conflicting */ 631 | margin-top: -10px; 632 | } 633 | 634 | pre, .typetable { 635 | border-radius: 0; 636 | border: none; 637 | /* codeFontSize */ 638 | font-size: 13px; 639 | } 640 | 641 | .constructor { 642 | /* colorRed */ 643 | color: #BF616A; 644 | } 645 | 646 | .keyword { 647 | /* colorKeyword (purple) */ 648 | color: #b48ead; 649 | } 650 | 651 | .type { 652 | color: #BF616A; 653 | } 654 | 655 | .string { 656 | color: #91B376; 657 | } 658 | 659 | pre { 660 | display: block; 661 | line-height: 20px; 662 | word-break: break-all; 663 | word-wrap: break-word; 664 | white-space: pre; 665 | white-space: pre-wrap; 666 | } 667 | 668 | pre.prettyprint { 669 | margin-bottom: 20px 670 | } 671 | 672 | code { 673 | white-space: nowrap; 674 | -webkit-border-radius: 0; 675 | -moz-border-radius: 0; 676 | -ms-border-radius: 0; 677 | -o-border-radius: 0; 678 | border-radius: 0 679 | } 680 | 681 | /** 682 | * code.code are typically inline code, in an info section - not code snippets. 683 | */ 684 | .info code.code, .param_info code.code { 685 | /* backgroundColorInlineCode */ 686 | background-color: #393d48; 687 | border-radius:1px; 688 | padding: 1px 3px 1px 3px; 689 | } 690 | 691 | /* Creates better underline for anything inside an anchor (better than standard 692 | * text decoration) that is inside an info or param_info. */ 693 | body > a > *, 694 | p > a > *, 695 | li > a > *, 696 | .info > a > *, 697 | .param_info > a > * { 698 | /* colorFadedText (comment) */ 699 | border-bottom: 1px dotted #EBCB8B; 700 | } 701 | 702 | body > a:hover > *, 703 | body > a:focus > *, 704 | p > a:hover > *, 705 | p > a:focus > *, 706 | li > a:hover > *, 707 | li > a:focus > *, 708 | .info > a:hover > *, 709 | .info > a:focus > *, 710 | .param_info > a:hover > *, 711 | .param_info > a:focus > * { 712 | /* colorYellow */ 713 | border-bottom: 1px solid #EBCB8B; 714 | } 715 | 716 | 717 | /** 718 | * Anchors appearing in pre or code do not contain more pre or code typically, 719 | * and usually only contain text: which is why we must target their anchor directly. 720 | * Which didn't work when the anchor had a code child (bottom borders do not render) 721 | */ 722 | pre > a, 723 | code > a, 724 | .typetable a { 725 | /* colorFadedText (comment) */ 726 | border-bottom: 1px dotted #EBCB8B; 727 | } 728 | 729 | pre > a:focus, 730 | pre > a:hover, 731 | code > a:focus, 732 | code > a:hover, 733 | .typetable a:focus, 734 | .typetable a:hover { 735 | /* colorYellow */ 736 | border-bottom: 1px solid #EBCB8B; 737 | } 738 | 739 | /** 740 | * For named arguments documentation: Give the named arguments a little pop. 741 | */ 742 | .param_info > code.code:nth-child(1) { 743 | /* backgroundColorInlineBorder */ 744 | border: 1px solid #474b56; 745 | } 746 | 747 | 748 | 749 | pre code { 750 | color: inherit; 751 | white-space: pre; 752 | white-space: pre-wrap; 753 | background-color: transparent; 754 | border: 0 755 | } 756 | 757 | h1, h2, h3, h4, h5, h6 { 758 | margin: 10px 0; 759 | font-weight: bold; 760 | line-height: 20px; 761 | color: inherit; 762 | text-rendering: optimizelegibility; 763 | } 764 | 765 | h1 { 766 | font-size: 22px; 767 | } 768 | 769 | h2 { 770 | font-size: 20px 771 | } 772 | 773 | h3 { 774 | font-size: 18px 775 | } 776 | 777 | h4 { 778 | font-size: 16px 779 | } 780 | 781 | h5 { 782 | font-size: 14px 783 | } 784 | 785 | /** 786 | * The ocamldoc generator uses these for primary headers: 787 | */ 788 | h6 { 789 | font-size: 18px; 790 | } 791 | 792 | /** 793 | * To fix the formatting, we hide all the brs, and add a bunch of space above the h6's. 794 | */ 795 | h6 { 796 | margin-top:40px; 797 | } 798 | 799 | /** 800 | * Ocamldoc generator uses .h7 as subheaders 801 | */ 802 | .h7 { 803 | /* bodyFontSize */ 804 | font-size: 14px; 805 | font-weight: bold; 806 | } 807 | 808 | body > h1 { 809 | /* backgroundColorDark */ 810 | background-color: #252932; 811 | margin-right: -40px; 812 | margin-left: -40px; 813 | padding-top: 10px; 814 | padding-left: 20px; 815 | padding-right: 20px; 816 | padding-bottom: 10px; 817 | } 818 | 819 | body > h1:first-of-type { 820 | text-align:right; 821 | position: fixed; 822 | z-index: 90; 823 | left: 0; 824 | right: 0; 825 | margin: 0; 826 | top: 0; 827 | } 828 | 829 | h1 > a { 830 | /* colorRed */ 831 | color: #BF616A; 832 | } 833 | h1 > a:active, h1 > a:focus, h1 > a:hover { 834 | /* colorRedActive */ 835 | color: #CB6D77; 836 | } 837 | 838 | .navbar { 839 | position: fixed; 840 | left: 0; right: 0; 841 | margin-top: -50px; /* Match body padding */ 842 | margin-bottom: 0px; 843 | padding-top: 0px; 844 | padding-left: 20px; 845 | padding-right: 20px; 846 | padding-bottom: 0px; 847 | z-index: 91; 848 | } 849 | 850 | 851 | .navbar > a { 852 | display: inline-block; 853 | font-weight: bold; 854 | color: #999; 855 | text-decoration: none; 856 | padding-top: 14px; 857 | padding-left: 5px; 858 | padding-right: 5px; 859 | height: 40px; 860 | border-bottom: 4px solid; 861 | } 862 | 863 | .navbar > a:nth-child(1n) { 864 | /* colorGreen */ 865 | border-bottom: 4px solid #91B376; 866 | } 867 | 868 | .navbar > a:nth-child(2n) { 869 | /* colorRed */ 870 | border-bottom: 4px solid #BF616A; 871 | } 872 | 873 | .navbar > a:nth-child(3n) { 874 | /* colorCyan */ 875 | border-bottom: 4px solid #96B5B4; 876 | } 877 | 878 | .navbar > a:nth-child(4n) { 879 | border-bottom: 4px solid #cec47f; 880 | } 881 | 882 | .navbar > a:nth-child(5n) { 883 | border-bottom: 4px solid #b58900; 884 | } 885 | 886 | 887 | .navbar a:hover, a:focus { 888 | opacity: 1.0; 889 | text-decoration: none 890 | } 891 | 892 | 893 | 894 | .indextable { 895 | /* backgroundColor */ 896 | background-color: #2b303b; 897 | } 898 | 899 | .indextable td { 900 | padding-left: 5px; 901 | padding-right: 5px; 902 | } 903 | 904 | .indexlist { 905 | margin-top: 12px; 906 | } 907 | 908 | 909 | body > .info { 910 | /* backgroundColorPanel */ 911 | background-color: #313641; 912 | padding: 20px; 913 | margin-left:20px; 914 | margin-top: 15px; 915 | /* So that the top level pre's 30px margin-top is halfed, but param info's 916 | * are sucked up into the top level .info's */ 917 | /* colorFadedText (comment) */ 918 | border-left: 5px solid rgba(101,115,126, 0.2); 919 | } 920 | 921 | 922 | 923 | body > .param_info { 924 | /* backgroundColorPanel */ 925 | margin-top:1px; 926 | background-color: #313641; 927 | margin-left: 20px; 928 | padding: 15px; 929 | /* colorGreen */ 930 | border-left: 5px solid #91B376; 931 | } 932 | 933 | pre + .info, table + .info { 934 | /* margin-bottom:15px; */ 935 | } 936 | 937 | /** 938 | * Each module entry should have proper spacing. 939 | */ 940 | body > pre { 941 | margin-top: 20px; 942 | margin-bottom: 10px; 943 | margin-left: 0px; 944 | margin-right: 0px; 945 | } 946 | 947 | 948 | .paramstable { 949 | /* codeFontSize */ 950 | font-size: 13px; 951 | } 952 | 953 | 954 | .info.module.top { 955 | /* By default, free floating text in the top info will have a lighter weight and larger size. 956 | * Then inner

tags style will then bring it back down to standard weight/size, but the first 957 | * free floating text which describes the module is *not* in a p tag so will have this nicer look */ 958 | font-size: 18px; 959 | /* colorText */ 960 | color: #c0c5ce; 961 | /* margin-left: -50px; */ 962 | /* margin-right: -50px; */ 963 | margin-left: 0px; 964 | } 965 | 966 | /* The first block of text */ 967 | .info.module.top > *:nth-child(1) { 968 | font-size: 16px; 969 | font-weight: 300; 970 | padding-bottom: 10px; 971 | } 972 | 973 | .info.module.top > code, .info.module.top > .code, 974 | .info.module.top > p > .code { 975 | /* background-color: #545454; */ 976 | } 977 | 978 | 979 | .info.module.top + hr { 980 | position: relative; 981 | font-weight: normal; 982 | font-size: 24px; 983 | border:none; 984 | line-height: normal; 985 | display: inline-block; 986 | margin-top: 0; 987 | margin-left: -25px; 988 | color: white; 989 | white-space: nowrap; 990 | -webkit-transition: all 0.1s ease-out; 991 | -moz-transition: all 0.1s ease-out; 992 | -o-transition: all 0.1s ease-out; 993 | transition: all 0.1s ease-out; 994 | opacity: 0.8; 995 | -webkit-backface-visibility: hidden 996 | } 997 | /** 998 | * See the Set documentation for why we need this: 999 | */ 1000 | .info > .codepre > code > br { 1001 | display:none; 1002 | } 1003 | .codepre { 1004 | padding:10px; 1005 | /* backgroundColorInlineCode */ 1006 | background-color: #393d48; 1007 | } 1008 | .info > .codepre > .code { 1009 | font-weight: normal; 1010 | /* No background color on inner code lines/elements because the outer .codepre has 1011 | * provided the background color. */ 1012 | background-color: transparent; 1013 | } 1014 | 1015 | /* .info.module.top + hr:after { */ 1016 | /* content: " "; */ 1017 | /* display: block; */ 1018 | /* border-top: 4px solid #3d3d3b; */ 1019 | /* opacity: 0.8; */ 1020 | /* width: 0; */ 1021 | /* height: 0; */ 1022 | /* position: absolute; */ 1023 | /* bottom: -4px; */ 1024 | /* border-left: 5px solid transparent; */ 1025 | /* left: 0 */ 1026 | /* } */ 1027 | /* */ 1028 | /* hr:after { */ 1029 | /* border-top-color: #b9221f; */ 1030 | /* } */ 1031 | 1032 | 1033 | pre, code, .code { 1034 | /* colorText */ 1035 | color: #c0c5ce; 1036 | } 1037 | 1038 | pre, code, .code, pre *, code *, .code * { 1039 | /* codeFontSize */ 1040 | font-size: 13px; 1041 | } 1042 | 1043 | .typefieldcomment { 1044 | visibility:hidden; 1045 | } 1046 | 1047 | .typefieldcomment > .info { 1048 | visibility: visible; 1049 | } 1050 | 1051 | pre { 1052 | padding-left: 0px; /* More to fix the trailing } */ 1053 | } 1054 | 1055 | .info, b, ol *, ul *, p { 1056 | /* bodyFontSize */ 1057 | font-size: 14px; 1058 | } 1059 | 1060 | 1061 | .navbar { 1062 | font-size: 18px; 1063 | } 1064 | 1065 | /* hacks for documents with .lstframe code listings */ 1066 | td[bgcolor=white] { 1067 | /* removes white blocks next to certain code listings */ 1068 | display: none; 1069 | } 1070 | 1071 | .lstframe { 1072 | background-color: transparent !important; 1073 | } 1074 | -------------------------------------------------------------------------------- /src/css/ocamlDoc.css: -------------------------------------------------------------------------------- 1 | /** 2 | * This CSS file for documentation generation embeds Normalize.css 3 | * https://necolas.github.io/normalize.css/4.1.1/normalize.css 4 | * 5 | * And then specifies a bunch of custom styles for the ocamldoc generation: 6 | * 7 | */ 8 | 9 | 10 | 11 | 12 | /* bodyFontSize */ 13 | font-size: 14px; 14 | 15 | /* codeFontSize */ 16 | font-size: 13px 17 | 18 | 19 | /* infoBackgroundColor */ 20 | background-color: #30302d; 21 | 22 | /* linkColor */ 23 | color: #cec47f; 24 | /* linkColorActive */ 25 | color: #faf0ab; 26 | 27 | 28 | 29 | /** 30 | * Space Gray Dark: 31 | * ================ 32 | */ 33 | /* backgroundColorDark */ 34 | background-color: #262b36; 35 | 36 | /* backgroundColor */ 37 | background-color: #2b303b; 38 | 39 | /* backgroundColorPanel */ 40 | background-color: #313641; 41 | 42 | /* borderLeftColorPanel */ 43 | border-left: 4px solid rgba(101,115,126, 0.2); 44 | 45 | /* borderColorInlineCode */ 46 | border: 1px solid #3b3f4a; 47 | 48 | /* backgroundColorInlineCode */ 49 | background-color: #393d48; 50 | 51 | /* backgroundColorInlineBorder */ 52 | border: 1px solid #474b56; 53 | 54 | /* colorText */ 55 | color: #c0c5ce; 56 | 57 | /* primaryColor */ 58 | color: #8fa1b3; 59 | 60 | /* text */ 61 | /* colorFadedText (comment) */ 62 | 5px solid rgba(101,115,126,1); 63 | 64 | /* let */ 65 | /* colorKeyword (purple) */ 66 | color: #b48ead; 67 | 68 | /* colorRed */ 69 | color: #BF616A; 70 | /* colorRedActive */ 71 | color: #CB6D77; 72 | 73 | /* colorGreen */ 74 | color: #91B376; 75 | 76 | /* colorGreenActive */ 77 | color: #97B98c; 78 | 79 | /* colorYellow */ 80 | color: #EBCB8B; 81 | 82 | /* colorYellowActive */ 83 | color: #FBDB9B; 84 | 85 | /* colorCyan */ 86 | color: #96B5B4; 87 | 88 | 89 | 90 | /* 91 | 92 | 93 | TopInfoBg: 94 | #424240 95 | 96 | TopInfoCodeBg: 97 | #545454 98 | 99 | 100 | InfoBgLeftBorder: 101 | #666664 102 | 103 | KeyWord (let type etc): 104 | #ecac9d 105 | 106 | Type: 107 | #ff0001; 108 | 109 | Comment: 110 | #aeaea1; 111 | 112 | 113 | .navbar > a:nth-child(1n) { 114 | border-bottom: 4px solid #d7df9d; 115 | } 116 | 117 | .navbar > a:nth-child(2n) { 118 | border-bottom: 4px solid #ef95a4; 119 | } 120 | 121 | .navbar > a:nth-child(3n) { 122 | border-bottom: 4px solid #aabbd0; 123 | } 124 | 125 | .navbar > a:nth-child(4n) { 126 | border-bottom: 4px solid #cec47f; 127 | } 128 | 129 | .navbar > a:nth-child(5n) { 130 | border-bottom: 4px solid #b58900; 131 | } 132 | 133 | 134 | /*! normalize.css v4.1.1 | MIT License | github.com/necolas/normalize.css */ 135 | 136 | /** 137 | * 1. Change the default font family in all browsers (opinionated). 138 | * 2. Prevent adjustments of font size after orientation changes in IE and iOS. 139 | */ 140 | 141 | html { 142 | font-family: sans-serif; /* 1 */ 143 | -ms-text-size-adjust: 100%; /* 2 */ 144 | -webkit-text-size-adjust: 100%; /* 2 */ 145 | } 146 | 147 | /** 148 | * Remove the margin in all browsers (opinionated). 149 | */ 150 | 151 | body { 152 | margin: 0; 153 | } 154 | 155 | /* HTML5 display definitions 156 | ========================================================================== */ 157 | 158 | /** 159 | * Add the correct display in IE 9-. 160 | * 1. Add the correct display in Edge, IE, and Firefox. 161 | * 2. Add the correct display in IE. 162 | */ 163 | 164 | article, 165 | aside, 166 | details, /* 1 */ 167 | figcaption, 168 | figure, 169 | footer, 170 | header, 171 | main, /* 2 */ 172 | menu, 173 | nav, 174 | section, 175 | summary { /* 1 */ 176 | display: block; 177 | } 178 | 179 | /** 180 | * Add the correct display in IE 9-. 181 | */ 182 | 183 | audio, 184 | canvas, 185 | progress, 186 | video { 187 | display: inline-block; 188 | } 189 | 190 | /** 191 | * Add the correct display in iOS 4-7. 192 | */ 193 | 194 | audio:not([controls]) { 195 | display: none; 196 | height: 0; 197 | } 198 | 199 | /** 200 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 201 | */ 202 | 203 | progress { 204 | vertical-align: baseline; 205 | } 206 | 207 | /** 208 | * Add the correct display in IE 10-. 209 | * 1. Add the correct display in IE. 210 | */ 211 | 212 | template, /* 1 */ 213 | [hidden] { 214 | display: none; 215 | } 216 | 217 | /* Links 218 | ========================================================================== */ 219 | 220 | /** 221 | * 1. Remove the gray background on active links in IE 10. 222 | * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. 223 | */ 224 | 225 | a { 226 | background-color: transparent; /* 1 */ 227 | -webkit-text-decoration-skip: objects; /* 2 */ 228 | } 229 | 230 | /** 231 | * Remove the outline on focused links when they are also active or hovered 232 | * in all browsers (opinionated). 233 | */ 234 | 235 | a:active, 236 | a:hover { 237 | outline-width: 0; 238 | } 239 | 240 | /* Text-level semantics 241 | ========================================================================== */ 242 | 243 | /** 244 | * 1. Remove the bottom border in Firefox 39-. 245 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 246 | */ 247 | 248 | abbr[title] { 249 | border-bottom: none; /* 1 */ 250 | text-decoration: underline; /* 2 */ 251 | text-decoration: underline dotted; /* 2 */ 252 | } 253 | 254 | /** 255 | * Prevent the duplicate application of `bolder` by the next rule in Safari 6. 256 | */ 257 | 258 | b, 259 | strong { 260 | font-weight: inherit; 261 | } 262 | 263 | /** 264 | * Add the correct font weight in Chrome, Edge, and Safari. 265 | */ 266 | 267 | b, 268 | strong { 269 | font-weight: bolder; 270 | } 271 | 272 | /** 273 | * Add the correct font style in Android 4.3-. 274 | */ 275 | 276 | dfn { 277 | font-style: italic; 278 | } 279 | 280 | /** 281 | * Correct the font size and margin on `h1` elements within `section` and 282 | * `article` contexts in Chrome, Firefox, and Safari. 283 | */ 284 | 285 | h1 { 286 | font-size: 2em; 287 | margin: 0.67em 0; 288 | } 289 | 290 | /** 291 | * Add the correct background and color in IE 9-. 292 | */ 293 | 294 | mark { 295 | background-color: #ff0; 296 | color: #000; 297 | } 298 | 299 | /** 300 | * Add the correct font size in all browsers. 301 | */ 302 | 303 | small { 304 | font-size: 80%; 305 | } 306 | 307 | /** 308 | * Prevent `sub` and `sup` elements from affecting the line height in 309 | * all browsers. 310 | */ 311 | 312 | sub, 313 | sup { 314 | font-size: 75%; 315 | line-height: 0; 316 | position: relative; 317 | vertical-align: baseline; 318 | } 319 | 320 | sub { 321 | bottom: -0.25em; 322 | } 323 | 324 | sup { 325 | top: -0.5em; 326 | } 327 | 328 | /* Embedded content 329 | ========================================================================== */ 330 | 331 | /** 332 | * Remove the border on images inside links in IE 10-. 333 | */ 334 | 335 | img { 336 | border-style: none; 337 | } 338 | 339 | /** 340 | * Hide the overflow in IE. 341 | */ 342 | 343 | svg:not(:root) { 344 | overflow: hidden; 345 | } 346 | 347 | /* Grouping content 348 | ========================================================================== */ 349 | 350 | /** 351 | * 1. Correct the inheritance and scaling of font size in all browsers. 352 | * 2. Correct the odd `em` font sizing in all browsers. 353 | */ 354 | 355 | code, 356 | kbd, 357 | pre, 358 | samp { 359 | font-family: monospace, monospace; /* 1 */ 360 | font-size: 1em; /* 2 */ 361 | } 362 | 363 | /** 364 | * Add the correct margin in IE 8. 365 | */ 366 | 367 | figure { 368 | margin: 1em 40px; 369 | } 370 | 371 | /** 372 | * 1. Add the correct box sizing in Firefox. 373 | * 2. Show the overflow in Edge and IE. 374 | */ 375 | 376 | hr { 377 | box-sizing: content-box; /* 1 */ 378 | height: 0; /* 1 */ 379 | overflow: visible; /* 2 */ 380 | } 381 | 382 | /* Forms 383 | ========================================================================== */ 384 | 385 | /** 386 | * 1. Change font properties to `inherit` in all browsers (opinionated). 387 | * 2. Remove the margin in Firefox and Safari. 388 | */ 389 | 390 | button, 391 | input, 392 | select, 393 | textarea { 394 | font: inherit; /* 1 */ 395 | margin: 0; /* 2 */ 396 | } 397 | 398 | /** 399 | * Restore the font weight unset by the previous rule. 400 | */ 401 | 402 | optgroup { 403 | font-weight: bold; 404 | } 405 | 406 | /** 407 | * Show the overflow in IE. 408 | * 1. Show the overflow in Edge. 409 | */ 410 | 411 | button, 412 | input { /* 1 */ 413 | overflow: visible; 414 | } 415 | 416 | /** 417 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 418 | * 1. Remove the inheritance of text transform in Firefox. 419 | */ 420 | 421 | button, 422 | select { /* 1 */ 423 | text-transform: none; 424 | } 425 | 426 | /** 427 | * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` 428 | * controls in Android 4. 429 | * 2. Correct the inability to style clickable types in iOS and Safari. 430 | */ 431 | 432 | button, 433 | html [type="button"], /* 1 */ 434 | [type="reset"], 435 | [type="submit"] { 436 | -webkit-appearance: button; /* 2 */ 437 | } 438 | 439 | /** 440 | * Remove the inner border and padding in Firefox. 441 | */ 442 | 443 | button::-moz-focus-inner, 444 | [type="button"]::-moz-focus-inner, 445 | [type="reset"]::-moz-focus-inner, 446 | [type="submit"]::-moz-focus-inner { 447 | border-style: none; 448 | padding: 0; 449 | } 450 | 451 | /** 452 | * Restore the focus styles unset by the previous rule. 453 | */ 454 | 455 | button:-moz-focusring, 456 | [type="button"]:-moz-focusring, 457 | [type="reset"]:-moz-focusring, 458 | [type="submit"]:-moz-focusring { 459 | outline: 1px dotted ButtonText; 460 | } 461 | 462 | /** 463 | * Change the border, margin, and padding in all browsers (opinionated). 464 | */ 465 | 466 | fieldset { 467 | border: 1px solid #c0c0c0; 468 | margin: 0 2px; 469 | padding: 0.35em 0.625em 0.75em; 470 | } 471 | 472 | /** 473 | * 1. Correct the text wrapping in Edge and IE. 474 | * 2. Correct the color inheritance from `fieldset` elements in IE. 475 | * 3. Remove the padding so developers are not caught out when they zero out 476 | * `fieldset` elements in all browsers. 477 | */ 478 | 479 | legend { 480 | box-sizing: border-box; /* 1 */ 481 | color: inherit; /* 2 */ 482 | display: table; /* 1 */ 483 | max-width: 100%; /* 1 */ 484 | padding: 0; /* 3 */ 485 | white-space: normal; /* 1 */ 486 | } 487 | 488 | /** 489 | * Remove the default vertical scrollbar in IE. 490 | */ 491 | 492 | textarea { 493 | overflow: auto; 494 | } 495 | 496 | /** 497 | * 1. Add the correct box sizing in IE 10-. 498 | * 2. Remove the padding in IE 10-. 499 | */ 500 | 501 | [type="checkbox"], 502 | [type="radio"] { 503 | box-sizing: border-box; /* 1 */ 504 | padding: 0; /* 2 */ 505 | } 506 | 507 | /** 508 | * Correct the cursor style of increment and decrement buttons in Chrome. 509 | */ 510 | 511 | [type="number"]::-webkit-inner-spin-button, 512 | [type="number"]::-webkit-outer-spin-button { 513 | height: auto; 514 | } 515 | 516 | /** 517 | * 1. Correct the odd appearance in Chrome and Safari. 518 | * 2. Correct the outline style in Safari. 519 | */ 520 | 521 | [type="search"] { 522 | -webkit-appearance: textfield; /* 1 */ 523 | outline-offset: -2px; /* 2 */ 524 | } 525 | 526 | /** 527 | * Remove the inner padding and cancel buttons in Chrome and Safari on OS X. 528 | */ 529 | 530 | [type="search"]::-webkit-search-cancel-button, 531 | [type="search"]::-webkit-search-decoration { 532 | -webkit-appearance: none; 533 | } 534 | 535 | /** 536 | * Correct the text style of placeholders in Chrome, Edge, and Safari. 537 | */ 538 | 539 | ::-webkit-input-placeholder { 540 | color: inherit; 541 | opacity: 0.54; 542 | } 543 | 544 | /** 545 | * 1. Correct the inability to style clickable types in iOS and Safari. 546 | * 2. Change font properties to `inherit` in Safari. 547 | */ 548 | 549 | ::-webkit-file-upload-button { 550 | -webkit-appearance: button; /* 1 */ 551 | font: inherit; /* 2 */ 552 | } 553 | 554 | 555 | 556 | 557 | /** 558 | * CUSTOM CSS: 559 | */ 560 | 561 | ul,ol { 562 | padding: 0; 563 | margin: 10 0 10px 25px 564 | } 565 | 566 | ul ul, ul ol, ol ol, ol ul { 567 | margin-bottom: 0; 568 | } 569 | 570 | li { 571 | line-height: 20px 572 | } 573 | 574 | * { 575 | box-sizing: border-box; 576 | } 577 | 578 | 579 | body { 580 | font-family: "Helvetica", "Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif; 581 | /* bodyFontSize */ 582 | font-size: 14px; 583 | margin: 0px; 584 | line-height: 20px; 585 | /* backgroundColor */ 586 | background-color: #2b303b; 587 | 588 | /* colorText */ 589 | color: #c0c5ce; 590 | padding: 40px; 591 | padding-top: 50px; 592 | } 593 | 594 | td.module { 595 | font-family: Menlo,Menlo,Consolas,"Courier New",monospace; 596 | /* codeFontSize */ 597 | font-size: 13px 598 | } 599 | 600 | 601 | code, pre { 602 | font-family: Menlo,Menlo,Consolas,"Courier New",monospace; 603 | /* codeFontSize */ 604 | font-size: 13px; 605 | color: #333; 606 | } 607 | 608 | a { 609 | color: inherit; 610 | /* colorYellow */ 611 | text-decoration: none; 612 | -webkit-transition: color 0.1s ease-out; 613 | -moz-transition: color 0.1s ease-out; 614 | -o-transition: color 0.1s ease-out; 615 | transition: color 0.1s ease-out; 616 | } 617 | 618 | a:hover,a:focus { 619 | color: inherit; 620 | /* colorYellowActive */ 621 | text-decoration: none; 622 | outline: none; 623 | -webkit-transition: color 0.1s ease-out; 624 | -moz-transition: color 0.1s ease-out; 625 | -o-transition: color 0.1s ease-out; 626 | transition: color 0.1s ease-out; 627 | } 628 | 629 | .typetable { 630 | /* So that top level pre's margin-bottom isn't conflicting */ 631 | margin-top: -10px; 632 | } 633 | 634 | pre, .typetable { 635 | border-radius: 0; 636 | border: none; 637 | /* codeFontSize */ 638 | font-size: 13px; 639 | } 640 | 641 | .constructor { 642 | /* colorRed */ 643 | color: #BF616A; 644 | } 645 | 646 | .keyword { 647 | /* colorKeyword (purple) */ 648 | color: #b48ead; 649 | } 650 | 651 | .type { 652 | color: #BF616A; 653 | } 654 | 655 | .string { 656 | color: #91B376; 657 | } 658 | 659 | pre { 660 | display: block; 661 | line-height: 20px; 662 | word-break: break-all; 663 | word-wrap: break-word; 664 | white-space: pre; 665 | white-space: pre-wrap; 666 | } 667 | 668 | pre.prettyprint { 669 | margin-bottom: 20px 670 | } 671 | 672 | code { 673 | white-space: nowrap; 674 | -webkit-border-radius: 0; 675 | -moz-border-radius: 0; 676 | -ms-border-radius: 0; 677 | -o-border-radius: 0; 678 | border-radius: 0 679 | } 680 | 681 | /** 682 | * code.code are typically inline code, in an info section - not code snippets. 683 | */ 684 | .info code.code, .param_info code.code { 685 | /* backgroundColorInlineCode */ 686 | background-color: #393d48; 687 | border-radius:1px; 688 | padding: 1px 3px 1px 3px; 689 | } 690 | 691 | /* Creates better underline for anything inside an anchor (better than standard 692 | * text decoration) that is inside an info or param_info. */ 693 | body > a > *, 694 | p > a > *, 695 | li > a > *, 696 | .info > a > *, 697 | .param_info > a > * { 698 | /* colorFadedText (comment) */ 699 | border-bottom: 1px dotted #EBCB8B; 700 | } 701 | 702 | body > a:hover > *, 703 | body > a:focus > *, 704 | p > a:hover > *, 705 | p > a:focus > *, 706 | li > a:hover > *, 707 | li > a:focus > *, 708 | .info > a:hover > *, 709 | .info > a:focus > *, 710 | .param_info > a:hover > *, 711 | .param_info > a:focus > * { 712 | /* colorYellow */ 713 | border-bottom: 1px solid #EBCB8B; 714 | } 715 | 716 | 717 | /** 718 | * Anchors appearing in pre or code do not contain more pre or code typically, 719 | * and usually only contain text: which is why we must target their anchor directly. 720 | * Which didn't work when the anchor had a code child (bottom borders do not render) 721 | */ 722 | pre > a, 723 | code > a, 724 | .typetable a { 725 | /* colorFadedText (comment) */ 726 | border-bottom: 1px dotted #EBCB8B; 727 | } 728 | 729 | pre > a:focus, 730 | pre > a:hover, 731 | code > a:focus, 732 | code > a:hover, 733 | .typetable a:focus, 734 | .typetable a:hover { 735 | /* colorYellow */ 736 | border-bottom: 1px solid #EBCB8B; 737 | } 738 | 739 | /** 740 | * For named arguments documentation: Give the named arguments a little pop. 741 | */ 742 | .param_info > code.code:nth-child(1) { 743 | /* backgroundColorInlineBorder */ 744 | border: 1px solid #474b56; 745 | } 746 | 747 | 748 | 749 | pre code { 750 | color: inherit; 751 | white-space: pre; 752 | white-space: pre-wrap; 753 | background-color: transparent; 754 | border: 0 755 | } 756 | 757 | h1, h2, h3, h4, h5, h6 { 758 | margin: 10px 0; 759 | font-weight: bold; 760 | line-height: 20px; 761 | color: inherit; 762 | text-rendering: optimizelegibility; 763 | } 764 | 765 | h1 { 766 | font-size: 22px; 767 | } 768 | 769 | h2 { 770 | font-size: 20px 771 | } 772 | 773 | h3 { 774 | font-size: 18px 775 | } 776 | 777 | h4 { 778 | font-size: 16px 779 | } 780 | 781 | h5 { 782 | font-size: 14px 783 | } 784 | 785 | /** 786 | * The ocamldoc generator uses these for primary headers: 787 | */ 788 | h6 { 789 | font-size: 18px; 790 | } 791 | 792 | /** 793 | * To fix the formatting, we hide all the brs, and add a bunch of space above the h6's. 794 | */ 795 | h6 { 796 | margin-top:40px; 797 | } 798 | 799 | /** 800 | * Ocamldoc generator uses .h7 as subheaders 801 | */ 802 | .h7 { 803 | /* bodyFontSize */ 804 | font-size: 14px; 805 | font-weight: bold; 806 | } 807 | 808 | body > h1 { 809 | /* backgroundColorDark */ 810 | background-color: #252932; 811 | margin-right: -40px; 812 | margin-left: -40px; 813 | padding-top: 10px; 814 | padding-left: 20px; 815 | padding-right: 20px; 816 | padding-bottom: 10px; 817 | } 818 | 819 | body > h1:first-of-type { 820 | text-align:right; 821 | position: fixed; 822 | z-index: 90; 823 | left: 0; 824 | right: 0; 825 | margin: 0; 826 | top: 0; 827 | } 828 | 829 | h1 > a { 830 | /* colorRed */ 831 | color: #BF616A; 832 | } 833 | h1 > a:active, h1 > a:focus, h1 > a:hover { 834 | /* colorRedActive */ 835 | color: #CB6D77; 836 | } 837 | 838 | .navbar { 839 | position: fixed; 840 | left: 0; right: 0; 841 | margin-top: -50px; /* Match body padding */ 842 | margin-bottom: 0px; 843 | padding-top: 0px; 844 | padding-left: 20px; 845 | padding-right: 20px; 846 | padding-bottom: 0px; 847 | z-index: 91; 848 | } 849 | 850 | 851 | .navbar > a { 852 | display: inline-block; 853 | font-weight: bold; 854 | color: #999; 855 | text-decoration: none; 856 | padding-top: 14px; 857 | padding-left: 5px; 858 | padding-right: 5px; 859 | height: 40px; 860 | border-bottom: 4px solid; 861 | } 862 | 863 | .navbar > a:nth-child(1n) { 864 | /* colorGreen */ 865 | border-bottom: 4px solid #91B376; 866 | } 867 | 868 | .navbar > a:nth-child(2n) { 869 | /* colorRed */ 870 | border-bottom: 4px solid #BF616A; 871 | } 872 | 873 | .navbar > a:nth-child(3n) { 874 | /* colorCyan */ 875 | border-bottom: 4px solid #96B5B4; 876 | } 877 | 878 | .navbar > a:nth-child(4n) { 879 | border-bottom: 4px solid #cec47f; 880 | } 881 | 882 | .navbar > a:nth-child(5n) { 883 | border-bottom: 4px solid #b58900; 884 | } 885 | 886 | 887 | .navbar a:hover, a:focus { 888 | opacity: 1.0; 889 | text-decoration: none 890 | } 891 | 892 | 893 | 894 | .indextable { 895 | /* backgroundColor */ 896 | background-color: #2b303b; 897 | } 898 | 899 | .indextable td { 900 | padding-left: 5px; 901 | padding-right: 5px; 902 | } 903 | 904 | .indexlist { 905 | margin-top: 12px; 906 | } 907 | 908 | 909 | body > .info { 910 | /* backgroundColorPanel */ 911 | background-color: #313641; 912 | padding: 20px; 913 | margin-left:20px; 914 | margin-top: 15px; 915 | /* So that the top level pre's 30px margin-top is halfed, but param info's 916 | * are sucked up into the top level .info's */ 917 | /* colorFadedText (comment) */ 918 | border-left: 5px solid rgba(101,115,126, 0.2); 919 | } 920 | 921 | 922 | 923 | body > .param_info { 924 | /* backgroundColorPanel */ 925 | margin-top:1px; 926 | background-color: #313641; 927 | margin-left: 20px; 928 | padding: 15px; 929 | /* colorGreen */ 930 | border-left: 5px solid #91B376; 931 | } 932 | 933 | pre + .info, table + .info { 934 | /* margin-bottom:15px; */ 935 | } 936 | 937 | /** 938 | * Each module entry should have proper spacing. 939 | */ 940 | body > pre { 941 | margin-top: 20px; 942 | margin-bottom: 10px; 943 | margin-left: 0px; 944 | margin-right: 0px; 945 | } 946 | 947 | 948 | .paramstable { 949 | /* codeFontSize */ 950 | font-size: 13px; 951 | } 952 | 953 | 954 | .info.module.top { 955 | /* By default, free floating text in the top info will have a lighter weight and larger size. 956 | * Then inner

tags style will then bring it back down to standard weight/size, but the first 957 | * free floating text which describes the module is *not* in a p tag so will have this nicer look */ 958 | font-size: 18px; 959 | /* colorText */ 960 | color: #c0c5ce; 961 | /* margin-left: -50px; */ 962 | /* margin-right: -50px; */ 963 | margin-left: 0px; 964 | } 965 | 966 | /* The first block of text */ 967 | .info.module.top > *:nth-child(1) { 968 | font-size: 16px; 969 | font-weight: 300; 970 | padding-bottom: 10px; 971 | } 972 | 973 | .info.module.top > code, .info.module.top > .code, 974 | .info.module.top > p > .code { 975 | /* background-color: #545454; */ 976 | } 977 | 978 | 979 | .info.module.top + hr { 980 | position: relative; 981 | font-weight: normal; 982 | font-size: 24px; 983 | border:none; 984 | line-height: normal; 985 | display: inline-block; 986 | margin-top: 0; 987 | margin-left: -25px; 988 | color: white; 989 | white-space: nowrap; 990 | -webkit-transition: all 0.1s ease-out; 991 | -moz-transition: all 0.1s ease-out; 992 | -o-transition: all 0.1s ease-out; 993 | transition: all 0.1s ease-out; 994 | opacity: 0.8; 995 | -webkit-backface-visibility: hidden 996 | } 997 | /** 998 | * See the Set documentation for why we need this: 999 | */ 1000 | .info > .codepre > code > br { 1001 | display:none; 1002 | } 1003 | .codepre { 1004 | padding:10px; 1005 | /* backgroundColorInlineCode */ 1006 | background-color: #393d48; 1007 | } 1008 | .info > .codepre > .code { 1009 | font-weight: normal; 1010 | /* No background color on inner code lines/elements because the outer .codepre has 1011 | * provided the background color. */ 1012 | background-color: transparent; 1013 | } 1014 | 1015 | /* .info.module.top + hr:after { */ 1016 | /* content: " "; */ 1017 | /* display: block; */ 1018 | /* border-top: 4px solid #3d3d3b; */ 1019 | /* opacity: 0.8; */ 1020 | /* width: 0; */ 1021 | /* height: 0; */ 1022 | /* position: absolute; */ 1023 | /* bottom: -4px; */ 1024 | /* border-left: 5px solid transparent; */ 1025 | /* left: 0 */ 1026 | /* } */ 1027 | /* */ 1028 | /* hr:after { */ 1029 | /* border-top-color: #b9221f; */ 1030 | /* } */ 1031 | 1032 | 1033 | pre, code, .code { 1034 | /* colorText */ 1035 | color: #c0c5ce; 1036 | } 1037 | 1038 | pre, code, .code, pre *, code *, .code * { 1039 | /* codeFontSize */ 1040 | font-size: 13px; 1041 | } 1042 | 1043 | .typefieldcomment { 1044 | visibility:hidden; 1045 | } 1046 | 1047 | .typefieldcomment > .info { 1048 | visibility: visible; 1049 | } 1050 | 1051 | pre { 1052 | padding-left: 0px; /* More to fix the trailing } */ 1053 | } 1054 | 1055 | .info, b, ol *, ul *, p { 1056 | /* bodyFontSize */ 1057 | font-size: 14px; 1058 | } 1059 | 1060 | 1061 | .navbar { 1062 | font-size: 18px; 1063 | } 1064 | 1065 | /* hacks for documents with .lstframe code listings */ 1066 | td[bgcolor=white] { 1067 | /* removes white blocks next to certain code listings */ 1068 | display: none; 1069 | } 1070 | 1071 | .lstframe { 1072 | background-color: transparent !important; 1073 | } 1074 | --------------------------------------------------------------------------------