blob: 1a0f624ba4c082f0407c5516c4ad6061a60f7d9d [file] [log] [blame]
-- Copyright 2020 The Fuchsia Authors. All rights reserved.
-- Use of this source code is governed by a BSD-style license that can be
-- found in the LICENSE file.
module Settings exposing
( Model
, Msg(..)
, Scheme
, decode
, decodeScheme
, defaultScheme
, encode
, getLayout
, init
, initCmd
, themeClass
, update
, view
)
{-| This module defines a settings form.
-}
import Dict exposing (Dict)
import Form exposing (Form)
import Html exposing (Html)
import Json.Decode as Decode
import Json.Encode as Encode
import Ports
import Regex
import Set exposing (Set)
import SplitPane
------------- MODEL ------------------------------------------------------------
type alias Model =
{ form : Form.Model
, preferredScheme : Scheme
}
type Scheme
= Light
| Dark
init : Scheme -> Model
init preferredScheme =
{ form =
Form.empty
|> Form.insertStringValue "theme" (defaultTheme preferredScheme)
, preferredScheme = preferredScheme
}
initCmd : Model -> Cmd msg
initCmd model =
applySettings model
defaultScheme : Scheme
defaultScheme =
Light
defaultTheme : Scheme -> String
defaultTheme scheme =
case scheme of
Light ->
"textmate"
Dark ->
"tomorrow_night"
getLayout : Model -> SplitPane.Layout
getLayout model =
case Form.getStringValue form "layout" model.form of
"horizontal" ->
SplitPane.Locked SplitPane.Horizontal
"vertical" ->
SplitPane.Locked SplitPane.Vertical
_ ->
SplitPane.Auto
getTheme : Model -> String
getTheme model =
Form.getStringValue form "theme" model.form
getScheme : String -> Scheme
getScheme theme =
if Set.member theme lightThemes then
Light
else
Dark
themeClass : Model -> String
themeClass model =
case Dict.get (getTheme model) themeClassSuffixes of
Just class ->
"ace-" ++ class
Nothing ->
""
form : Form
form =
Form.define
[ Form.select "layout" "Layout" layouts "auto"
, Form.groupedSelect "theme" "Theme" themes "textmate"
, Form.select "keyboardHandler" "Keybindings" keybindings "ace"
, Form.number "refreshDelay" "Refresh delay (ms)" 500 Form.positive
, Form.number "fontSize" "Font size" 14 Form.positive
, Form.checkbox "wrap" "Wrap lines" False
, Form.checkbox "indentedSoftWrap" "Indent wrapped lines" False
, Form.number "tabSize" "Tab width" 4 Form.positive
, Form.checkbox "useSoftTabs" "Soft tabs" True
, Form.checkbox "atomicSoftTabs" "Atomic soft tabs" True
, Form.checkbox "showGutter" "Show gutter" True
, Form.checkbox "showLineNumbers" "Line numbers" True
, Form.checkbox "relativeLineNumbers" "Relative line numbers" False
, Form.checkbox "highlightActiveLine" "Highlight active line" True
, Form.checkbox "scrollPastEnd" "Scroll past end" False
, Form.select "foldStyle" "Fold markers" foldStyles "markbegin"
, Form.checkbox "displayIndentGuides" "Show indent guides" True
, Form.checkbox "showInvisibles" "Show invisibles" False
, Form.checkbox "showPrintMargin" "Show margin" False
, Form.number "printMarginColumn" "Margin column" 80 Form.nonnegative
, Form.text "selectionBackground"
"Selection background"
""
{ validate =
Regex.contains
(Maybe.withDefault Regex.never <|
Regex.fromString "^$|^#(|[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
)
, placeholder = Just "#ff0"
, invalidMessage = "Invalid color. Try a CSS hex color like \"#ff0\" or \"#d7f05d\"."
}
]
layouts : List ( Form.Identifier, String )
layouts =
[ ( "auto", "Auto" )
, ( "horizontal", "Horizontal" )
, ( "vertical", "Vertical" )
]
themes : List ( String, List ( Form.Identifier, String ) )
themes =
[ ( "Light"
, [ ( "chrome", "Chrome" )
, ( "clouds", "Clouds" )
, ( "crimson_editor", "Crimson Editor" )
, ( "dawn", "Dawn" )
, ( "dreamweaver", "Dreamweaver" )
, ( "eclipse", "Eclipse" )
, ( "github", "GitHub" )
, ( "iplastic", "iPlastic" )
, ( "katzenmilch", "KatzenMilch" )
, ( "kuroir", "Kuroir" )
, ( "solarized_light", "Solarized Light" )
, ( "sqlserver", "SQL Server" )
, ( "textmate", "TextMate" )
, ( "tomorrow", "Tomorrow" )
, ( "xcode", "Xcode" )
]
)
, ( "Dark"
, [ ( "ambiance", "Ambiance" )
, ( "chaos", "Chaos" )
, ( "clouds_midnight", "Clouds Midnight" )
, ( "cobalt", "Cobalt" )
, ( "dracula", "Dracula" )
, ( "gob", "Gob" )
, ( "gruvbox", "Gruvbox" )
, ( "idle_fingers", "idleFingers" )
, ( "kr_theme", "krTheme" )
, ( "merbivore", "Merbivore" )
, ( "merbivore_soft", "Merbivore Soft" )
, ( "mono_industrial", "Mono Industrial" )
, ( "monokai", "Monokai" )
, ( "pastel_on_dark", "Pastel on Dark" )
, ( "solarized_dark", "Solarized Dark" )
, ( "terminal", "Terminal" )
, ( "tomorrow_night", "Tomorrow Night" )
, ( "tomorrow_night_blue", "Tomorrow Night Blue" )
, ( "tomorrow_night_bright", "Tomorrow Night Bright" )
, ( "tomorrow_night_eighties", "Tomorrow Night 80s" )
, ( "twilight", "Twilight" )
, ( "vibrant_ink", "Vibrant Ink" )
]
)
]
lightThemes : Set Form.Identifier
lightThemes =
Set.fromList
[ "chrome"
, "clouds"
, "crimson_editor"
, "dawn"
, "dreamweaver"
, "eclipse"
, "github"
, "iplastic"
, "katzenmilch"
, "kuroir"
, "solarized_light"
, "sqlserver"
, "textmate"
, "tomorrow"
, "xcode"
]
themeClassSuffixes : Dict Form.Identifier String
themeClassSuffixes =
Dict.fromList
[ ( "ambiance", "ambiance" )
, ( "chaos", "chaos" )
, ( "chrome", "chrome" )
, ( "clouds", "clouds" )
, ( "clouds_midnight", "clouds-midnight" )
, ( "cobalt", "cobalt" )
, ( "crimson_editor", "crimson-editor" )
, ( "dawn", "dawn" )
, ( "dracula", "dracula" )
, ( "dreamweaver", "dreamweaver" )
, ( "eclipse", "eclipse" )
, ( "github", "github" )
, ( "gob", "gob" )
, ( "gruvbox", "gruvbox" )
, ( "idle_fingers", "idle-fingers" )
, ( "iplastic", "iplastic" )
, ( "katzenmilch", "katzenmilch" )
, ( "kr_theme", "kr-theme" )
, ( "kuroir", "kuroir" )
, ( "merbivore", "merbivore" )
, ( "merbivore_soft", "merbivore-soft" )
, ( "mono_industrial", "mono-industrial" )
, ( "monokai", "monokai" )
, ( "pastel_on_dark", "pastel-on-dark" )
, ( "solarized_dark", "solarized-dark" )
, ( "solarized_light", "solarized-light" )
, ( "sqlserver", "sqlserver" )
, ( "terminal", "terminal-theme" )
, ( "textmate", "tm" )
, ( "tomorrow", "tomorrow" )
, ( "tomorrow_night", "tomorrow-night" )
, ( "tomorrow_night_blue", "tomorrow-night-blue" )
, ( "tomorrow_night_bright", "tomorrow-night-bright" )
, ( "tomorrow_night_eighties", "tomorrow-night-eighties" )
, ( "twilight", "twilight" )
, ( "vibrant_ink", "vibrant-ink" )
, ( "xcode", "xcode" )
]
keybindings : List ( Form.Identifier, String )
keybindings =
[ ( "ace", "Ace" )
, ( "emacs", "Emacs" )
, ( "sublime", "Sublime Text" )
, ( "vim", "Vim" )
, ( "vscode", "VS Code" )
]
foldStyles : List ( Form.Identifier, String )
foldStyles =
[ ( "manual", "Off" )
, ( "markbegin", "Begin" )
, ( "markbeginend", "Begin and end" )
]
------------- UPDATE -----------------------------------------------------------
type Msg
= FormMsg Form.Msg
| ResetForm
| ClearDataAndReload
update : Msg -> Model -> ( Model, Cmd msg )
update msg model =
case msg of
FormMsg formMsg ->
let
newModel =
{ model | form = Form.update formMsg model.form }
in
( newModel
, applySettings newModel
)
ResetForm ->
let
newModel =
init model.preferredScheme
in
( newModel
, applySettings newModel
)
ClearDataAndReload ->
( model, Ports.clearDataAndReload )
applySettings : Model -> Cmd msg
applySettings model =
let
augmentedModel =
model.form
|> Form.insertStringValue "scheme"
(schemeToString (getScheme (getTheme model)))
|> Form.insertStringValue "themeClass"
(themeClass model)
in
Ports.applySettings (Form.encodeEffective form augmentedModel)
------------- VIEW -------------------------------------------------------------
view : (Msg -> msg) -> Model -> Html msg
view toMsg model =
Form.view form (buttons toMsg) (toMsg << FormMsg) model.form
buttons : (Msg -> msg) -> List (Form.Button msg)
buttons toMsg =
[ ( "Use defaults", toMsg ResetForm )
-- Labelling it "Reset" instead of "Clear" because, while it does clear the
-- local storage items, they will immediately get set again to their default
-- values when the page reloads.
, ( "Reset all data", toMsg ClearDataAndReload )
]
------------- ENCODE / DECODE --------------------------------------------------
encode : Model -> Encode.Value
encode model =
Form.encode model.form
decode : Decode.Decoder Model
decode =
Decode.map2 Model
(Form.decode form)
(Decode.succeed defaultScheme)
schemeToString : Scheme -> String
schemeToString scheme =
case scheme of
Light ->
"light"
Dark ->
"dark"
decodeScheme : Decode.Decoder Scheme
decodeScheme =
Decode.string
|> Decode.andThen
(\string ->
case schemeFromString string of
Just scheme ->
Decode.succeed scheme
_ ->
Decode.fail ("Invalid scheme: " ++ string)
)
schemeFromString : String -> Maybe Scheme
schemeFromString string =
case string of
"light" ->
Just Light
"dark" ->
Just Dark
_ ->
Nothing