elm-review/demo/Main.elm

342 lines
9.3 KiB
Elm

module Main exposing (main)
import Browser
import Html exposing (Html, button, div, input, label, p, text, textarea)
import Html.Attributes as Attr
import Html.Events as Events
import NoDebug
import NoUnused.CustomTypeConstructors
import NoUnused.Variables
import Reporter
import Review
import Review.Project as Project
import Review.Rule exposing (Rule)
-- MAIN
main : Program () Model Msg
main =
Browser.sandbox
{ init = init
, update = update
, view = view
}
-- MODEL
type alias Model =
{ sourceCode : String
, reviewErrors : List Review.Error
, noDebugEnabled : Bool
, noUnusedVariablesEnabled : Bool
, noUnusedTypeConstructorsEnabled : Bool
, showConfigurationAsText : Bool
}
init : Model
init =
let
sourceCode : String
sourceCode =
"""module Main exposing (f)
import NotUsed
import SomeModule exposing (notUsed)
type SomeCustomType
= UsedConstructor
| NotUsedConstructor
f : Int -> SomeCustomType
f x =
let
_ = Debug.log "x" x
in
UsedConstructor
g n = n + 1
"""
in
{ sourceCode = sourceCode
, reviewErrors = []
, noDebugEnabled = True
, noUnusedVariablesEnabled = True
, noUnusedTypeConstructorsEnabled = True
, showConfigurationAsText = False
}
|> runReview
-- REVIEW CONFIGURATION
config : Model -> List Rule
config model =
[ ( model.noDebugEnabled, NoDebug.rule )
, ( model.noUnusedVariablesEnabled, NoUnused.Variables.rule )
, ( model.noUnusedTypeConstructorsEnabled, NoUnused.CustomTypeConstructors.rule )
]
|> List.filter Tuple.first
|> List.map Tuple.second
-- UPDATE
type Msg
= UserEditedSourceCode String
| UserToggledNoDebugRule
| UserToggledNoUnusedVariablesRule
| UserToggledNoUnusedTypeConstructorsRule
| UserToggledConfigurationAsText
update : Msg -> Model -> Model
update action model =
case action of
UserEditedSourceCode sourceCode ->
{ model | sourceCode = sourceCode }
|> runReview
UserToggledNoDebugRule ->
{ model | noDebugEnabled = not model.noDebugEnabled }
|> runReview
UserToggledNoUnusedVariablesRule ->
{ model | noUnusedVariablesEnabled = not model.noUnusedVariablesEnabled }
|> runReview
UserToggledNoUnusedTypeConstructorsRule ->
{ model | noUnusedTypeConstructorsEnabled = not model.noUnusedTypeConstructorsEnabled }
|> runReview
UserToggledConfigurationAsText ->
{ model | showConfigurationAsText = not model.showConfigurationAsText }
runReview : Model -> Model
runReview model =
{ model | reviewErrors = Review.review (config model) Project.new (file model.sourceCode) }
-- VIEW
view : Model -> Html Msg
view model =
div [ Attr.id "wrapper" ]
[ div [ Attr.id "left" ]
[ p [ Attr.class "title" ] [ text "Source code" ]
, div
[ Attr.style "display" "flex"
, Attr.style "flex-direction" "row"
]
[ 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"
]
[ viewReviewErrors model
]
]
, div
[ Attr.style "margin-left" "2rem"
, Attr.style "width" "40%"
]
[ viewConfigurationPanel model
, viewConfigurationAsText model
, p [ Attr.class "title" ] [ text "Review errors" ]
]
]
]
]
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 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_ = "NoDebug"
, configExpression = "NoDebug.rule"
}
)
, ( model.noUnusedVariablesEnabled
, { import_ = "NoUnused.Variables"
, configExpression = "NoUnused.Variables.rule"
}
)
, ( model.noUnusedTypeConstructorsEnabled
, { import_ = "NoUnused.CustomTypeConstructors"
, configExpression = "NoUnused.CustomTypeConstructors.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
|> List.map (\{ configExpression } -> " " ++ configExpression)
|> String.join "\n ,"
in
"""module ReviewConfig exposing (config)
import Review.Rule exposing (Rule)
""" ++ importStatements ++ """
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
]
viewReviewErrors : Model -> Html msg
viewReviewErrors model =
reviewErrors model
|> List.map viewPart
|> Html.div []
viewPart : Reporter.TextContent -> Html msg
viewPart { str, color, backgroundColor } =
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 []
, case backgroundColor of
Just ( red, green, blue ) ->
Attr.style "background-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 [] [])
)
reviewErrors : Model -> List Reporter.TextContent
reviewErrors model =
Reporter.formatReport Reporter.Reviewing
[ ( file model.sourceCode
, model.reviewErrors
|> List.map fromReviewError
)
]
fromReviewError : Review.Error -> Reporter.Error
fromReviewError error =
{ moduleName = Review.errorModuleName error
, ruleName = Review.errorRuleName error
, message = Review.errorMessage error
, details = Review.errorDetails error
, range = Review.errorRange error
, hasFix = Review.errorFixes error /= Nothing
}
file : String -> { path : String, source : String }
file source =
{ path = "SOURCE CODE", source = source }