elm-review/example/Main.elm

381 lines
12 KiB
Elm
Raw Normal View History

module Main exposing (main)
2018-11-05 21:06:03 +03:00
import Browser
import Html exposing (Html, button, div, input, label, p, text, textarea)
import Html.Attributes as Attr
import Html.Events as Events
2019-07-25 15:35:58 +03:00
import Lint exposing (LintError, lintSource)
2019-06-24 23:55:16 +03:00
import Lint.Rule exposing (Rule)
2018-11-11 01:43:58 +03:00
import Lint.Rule.NoDebug
2019-06-16 16:31:40 +03:00
import Lint.Rule.NoExtraBooleanComparison
2019-06-03 01:30:24 +03:00
import Lint.Rule.NoImportingEverything
2019-07-05 00:57:55 +03:00
import Lint.Rule.NoUnusedTypeConstructors
2018-11-22 21:19:19 +03:00
import Lint.Rule.NoUnusedVariables
import Reporter
2019-06-24 02:20:10 +03:00
-- LINT CONFIGURATION
2019-07-25 15:35:58 +03:00
config : Model -> List Rule
config model =
2019-07-25 15:35:58 +03:00
[ ( model.noDebugEnabled, Lint.Rule.NoDebug.rule )
, ( model.noUnusedVariablesEnabled, Lint.Rule.NoUnusedVariables.rule )
, ( model.noImportingEverythingEnabled, Lint.Rule.NoImportingEverything.rule { exceptions = [ "Html" ] } )
, ( model.noExtraBooleanComparisonEnabled, Lint.Rule.NoExtraBooleanComparison.rule )
, ( model.noUnusedTypeConstructorsEnabled, Lint.Rule.NoUnusedTypeConstructors.rule )
-- , Lint.Rule.NoConstantCondition.rule
-- , Lint.Rule.NoDuplicateImports.rule
-- , Lint.Rule.NoExposingEverything.rule
-- , Lint.Rule.NoNestedLet.rule
-- , Lint.Rule.NoUnannotatedFunction.rule
-- , Lint.Rule.NoUselessIf.rule
-- , Lint.Rule.NoUselessPatternMatching.rule
-- , Lint.Rule.NoWarningComments.rule
-- , Lint.Rule.SimplifyPiping.rule
-- , Lint.Rule.SimplifyPropertyAccess.rule
-- , Lint.Rule.ElmTest.NoDuplicateTestBodies.rule
2017-01-20 01:31:55 +03:00
]
|> List.filter Tuple.first
|> List.map Tuple.second
2017-01-20 01:31:55 +03:00
2019-06-24 02:20:10 +03:00
-- MODEL
type alias Model =
{ sourceCode : String
, lintErrors : List LintError
, noDebugEnabled : Bool
, noUnusedVariablesEnabled : Bool
, noImportingEverythingEnabled : Bool
2019-07-03 19:26:53 +03:00
, noImportingEverythingExceptions : List String
, noExtraBooleanComparisonEnabled : Bool
2019-07-05 00:57:55 +03:00
, noUnusedTypeConstructorsEnabled : Bool
, showConfigurationAsText : Bool
2019-06-24 02:20:10 +03:00
}
2019-06-24 02:00:22 +03:00
init : Model
init =
2019-06-24 02:00:22 +03:00
let
sourceCode : String
sourceCode =
"""module Main exposing (f)
2017-01-19 23:02:21 +03:00
2019-06-03 01:30:24 +03:00
import Html.Events exposing (..)
2017-01-20 00:27:11 +03:00
import Html exposing (..)
2019-06-24 02:00:22 +03:00
import NotUsed
import SomeModule exposing (notUsed)
f : Int -> Int
2017-01-07 23:17:01 +03:00
f x = x Debug.log 1
2017-01-19 23:02:21 +03:00
g n = n + 1
"""
tmpModel : Model
tmpModel =
{ sourceCode = sourceCode
, lintErrors = []
, noDebugEnabled = True
, noUnusedVariablesEnabled = True
, noImportingEverythingEnabled = True
2019-07-03 19:26:53 +03:00
, noImportingEverythingExceptions = [ "Html", "Html.Attributes" ]
, noExtraBooleanComparisonEnabled = True
2019-07-05 00:57:55 +03:00
, noUnusedTypeConstructorsEnabled = True
, showConfigurationAsText = False
}
2019-06-24 02:00:22 +03:00
in
{ tmpModel | lintErrors = lintSource (config tmpModel) { fileName = "", source = sourceCode } }
2019-06-24 02:00:22 +03:00
2019-06-24 02:20:10 +03:00
-- UPDATE
type Msg
= UserEditedSourceCode String
| UserToggledNoDebugRule
| UserToggledNoUnusedVariablesRule
| UserToggledNoImportingEverythingRule
| UserToggledNoExtraBooleanComparisonRule
2019-07-05 00:57:55 +03:00
| UserToggledNoUnusedTypeConstructorsRule
| UserToggledConfigurationAsText
2019-06-24 02:00:22 +03:00
update : Msg -> Model -> Model
update action model =
case action of
2019-06-24 02:11:50 +03:00
UserEditedSourceCode sourceCode ->
2019-06-24 02:00:22 +03:00
{ model
| sourceCode = sourceCode
, lintErrors = lintSource (config model) { fileName = "Source code", source = sourceCode }
2019-06-24 02:00:22 +03:00
}
UserToggledNoDebugRule ->
{ model | noDebugEnabled = not model.noDebugEnabled }
|> rerunLinting
UserToggledNoUnusedVariablesRule ->
{ model | noUnusedVariablesEnabled = not model.noUnusedVariablesEnabled }
|> rerunLinting
UserToggledNoImportingEverythingRule ->
{ model | noImportingEverythingEnabled = not model.noImportingEverythingEnabled }
|> rerunLinting
UserToggledNoExtraBooleanComparisonRule ->
{ model | noExtraBooleanComparisonEnabled = not model.noExtraBooleanComparisonEnabled }
|> rerunLinting
2019-07-05 00:57:55 +03:00
UserToggledNoUnusedTypeConstructorsRule ->
{ model | noUnusedTypeConstructorsEnabled = not model.noUnusedTypeConstructorsEnabled }
|> rerunLinting
UserToggledConfigurationAsText ->
{ model | showConfigurationAsText = not model.showConfigurationAsText }
rerunLinting : Model -> Model
rerunLinting model =
{ model
| lintErrors =
lintSource (config model)
{ fileName = "Source code", source = model.sourceCode }
}
2019-06-24 02:20:10 +03:00
-- VIEW
2017-01-07 22:58:41 +03:00
2019-06-24 02:00:22 +03:00
view : Model -> Html Msg
view model =
div [ Attr.id "wrapper" ]
[ div [ Attr.id "left" ]
[ p [ Attr.class "title" ] [ text "Source code" ]
2019-06-24 02:11:18 +03:00
, div
[ Attr.style "display" "flex"
, Attr.style "flex-direction" "row"
2017-01-16 00:57:03 +03:00
]
[ div
[ Attr.style "width" "60%"
]
[ textarea
[ Attr.id "input"
, Events.onInput UserEditedSourceCode
, Attr.style "width" "100%"
, Attr.style "height" "500px"
]
[ text model.sourceCode ]
, div
[ Attr.style "border-radius" "4px"
, Attr.style "padding" "12px"
, Attr.style "max-width" "100%"
, Attr.style "width" "calc(100vw - 24px)"
, Attr.style "overflow-x" "auto"
, Attr.style "white-space" "pre"
, Attr.style "color" "white"
, Attr.style "font-family" "'Source Code Pro', monospace"
, Attr.style "font-size" "12px"
, Attr.style "background-color" "black"
]
[ viewLintErrors model
]
]
, div
[ Attr.style "margin-left" "2rem"
, Attr.style "width" "40%"
2019-06-24 02:11:18 +03:00
]
[ viewConfigurationPanel model
, viewConfigurationAsText model
, p [ Attr.class "title" ] [ text "Linting errors" ]
2019-06-24 02:11:18 +03:00
]
2017-01-16 00:57:03 +03:00
]
]
]
viewConfigurationPanel : Model -> Html Msg
viewConfigurationPanel model =
div []
[ p [ Attr.class "title" ] [ text "Configuration" ]
, div
[ Attr.style "display" "flex"
, Attr.style "flex-direction" "column"
]
[ viewCheckbox UserToggledNoDebugRule "NoDebug" model.noDebugEnabled
, viewCheckbox UserToggledNoUnusedVariablesRule "NoUnusedVariables" model.noUnusedVariablesEnabled
, viewCheckbox UserToggledNoImportingEverythingRule "NoImportingEverything" model.noImportingEverythingEnabled
, viewCheckbox UserToggledNoExtraBooleanComparisonRule "NoExtraBooleanComparison" model.noExtraBooleanComparisonEnabled
2019-07-05 00:57:55 +03:00
, viewCheckbox UserToggledNoUnusedTypeConstructorsRule "NoUnusedTypeConstructors" model.noUnusedTypeConstructorsEnabled
]
]
viewConfigurationAsText : Model -> Html Msg
viewConfigurationAsText model =
if model.showConfigurationAsText then
div
[ Attr.style "display" "flex"
, Attr.style "flex-direction" "column"
, Attr.style "width" "100%"
]
[ button
[ Attr.style "margin-top" "2rem"
, Events.onClick UserToggledConfigurationAsText
]
[ text "Hide configuration as Elm code" ]
, textarea
[ Events.onInput UserEditedSourceCode
, Attr.style "height" "300px"
, Attr.style "width" "100%"
]
[ text <| configurationAsText model ]
]
else
button
[ Attr.style "margin-top" "2rem"
, Events.onClick UserToggledConfigurationAsText
]
[ text "Show configuration as Elm code" ]
configurationAsText : Model -> String
configurationAsText model =
let
rules : List { import_ : String, configExpression : String }
rules =
[ ( model.noDebugEnabled
, { import_ = "Lint.Rule.NoDebug"
, configExpression = "Lint.Rule.NoDebug.rule"
}
)
, ( model.noUnusedVariablesEnabled
, { import_ = "Lint.Rule.NoUnusedVariables"
, configExpression = "Lint.Rule.NoUnusedVariables.rule"
}
)
, ( model.noImportingEverythingEnabled
, { import_ = "Lint.Rule.NoImportingEverything"
, configExpression = "Lint.Rule.NoImportingEverything.rule { exceptions = [] }"
}
)
, ( model.noExtraBooleanComparisonEnabled
, { import_ = "Lint.Rule.NoExtraBooleanComparison"
, configExpression = "Lint.Rule.NoExtraBooleanComparison.rule"
}
)
2019-07-05 00:57:55 +03:00
, ( model.noUnusedTypeConstructorsEnabled
, { import_ = "Lint.Rule.NoUnusedTypeConstructors"
, configExpression = "Lint.Rule.NoUnusedTypeConstructors.rule"
}
)
]
|> List.filter Tuple.first
|> List.map Tuple.second
importStatements : String
importStatements =
rules
|> List.map (\{ import_ } -> "import " ++ import_)
|> String.join "\n"
configExpressions : String
configExpressions =
rules
2019-07-25 15:35:58 +03:00
|> List.map (\{ configExpression } -> " " ++ configExpression)
|> String.join "\n ,"
in
"""module LintConfig exposing (config)
import Lint.Rule exposing (Rule)
""" ++ importStatements ++ """
2019-07-25 15:35:58 +03:00
config : List Rule
config =
[""" ++ configExpressions ++ """
]
"""
viewCheckbox : Msg -> String -> Bool -> Html Msg
viewCheckbox onClick name checked =
label
[]
[ input
[ Attr.type_ "checkbox"
, Attr.checked checked
, Events.onClick onClick
]
[]
, text name
]
viewLintErrors : Model -> Html msg
viewLintErrors model =
lintErrors model
|> List.map viewPart
|> Html.div []
viewPart : { str : String, color : Maybe ( Int, Int, Int ) } -> Html msg
viewPart { str, color } =
Html.span
[ case color of
Just ( red, green, blue ) ->
Attr.style "color" <| "rgb(" ++ String.fromInt red ++ "," ++ String.fromInt green ++ "," ++ String.fromInt blue ++ ")"
Nothing ->
Attr.classList []
]
(str
|> String.lines
|> List.map Html.text
|> List.intersperse (Html.br [] [])
)
lintErrors : Model -> List { str : String, color : Maybe ( Int, Int, Int ) }
2019-06-24 02:20:10 +03:00
lintErrors model =
if List.isEmpty model.lintErrors then
[ { str = "I found no linting errors.\nYou're all good!"
, color = Nothing
}
]
else
[ ( { name = "Source code"
, source = model.sourceCode
}
, model.lintErrors
|> List.map fromLintError
)
]
|> Reporter.formatReport
fromLintError : LintError -> Reporter.Error
fromLintError error =
{ ruleName = Lint.errorRuleName error
, message = Lint.errorMessage error
, details = Lint.errorDetails error
, range = Lint.errorRange error
}
2019-06-24 02:20:10 +03:00
2019-06-24 02:00:22 +03:00
main : Program () Model Msg
main =
2018-11-05 21:06:03 +03:00
Browser.sandbox
2018-11-05 21:30:47 +03:00
{ init = init
, update = update
, view = view
}