elm-review/demo/Main.elm

350 lines
9.6 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-08-29 15:37:22 +03:00
import NoDebug
import NoUnused.CustomTypeConstructors
import NoUnused.Variables
import Reporter
import Review
import Review.Project as Project
import Review.Rule exposing (Rule)
2019-06-24 02:20:10 +03:00
2019-07-31 00:29:39 +03:00
-- MAIN
2019-07-25 15:35:58 +03:00
2019-07-31 00:29:39 +03:00
main : Program () Model Msg
main =
Browser.sandbox
{ init = init
, update = update
, view = view
}
2017-01-20 01:31:55 +03:00
2019-06-24 02:20:10 +03:00
-- MODEL
type alias Model =
{ sourceCode : String
, reviewErrors : List Review.Error
, noDebugEnabled : Bool
, noUnusedVariablesEnabled : 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-24 02:00:22 +03:00
import NotUsed
import SomeModule exposing (notUsed)
type SomeCustomType
= UsedConstructor
| NotUsedConstructor
f : Int -> SomeCustomType
f x =
let
_ = Debug.log "x" x
in
UsedConstructor
2017-01-19 23:02:21 +03:00
g n = n + 1
"""
2019-06-24 02:00:22 +03:00
in
{ sourceCode = sourceCode
, reviewErrors = []
, noDebugEnabled = True
, noUnusedVariablesEnabled = True
, noUnusedTypeConstructorsEnabled = True
, showConfigurationAsText = False
}
|> runReview
2019-06-24 02:00:22 +03:00
2019-06-24 02:20:10 +03:00
-- REVIEW CONFIGURATION
2019-07-31 00:29:39 +03:00
config : Model -> List Rule
config model =
2019-08-29 15:37:22 +03:00
[ ( model.noDebugEnabled, NoDebug.rule )
, ( model.noUnusedVariablesEnabled, NoUnused.Variables.rule )
, ( model.noUnusedTypeConstructorsEnabled, NoUnused.CustomTypeConstructors.rule )
2019-07-31 00:29:39 +03:00
]
|> List.filter Tuple.first
|> List.map Tuple.second
2019-06-24 02:20:10 +03:00
-- UPDATE
type Msg
= UserEditedSourceCode String
| UserToggledNoDebugRule
| UserToggledNoUnusedVariablesRule
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 ->
{ model | sourceCode = sourceCode }
|> runReview
UserToggledNoDebugRule ->
{ model | noDebugEnabled = not model.noDebugEnabled }
|> runReview
UserToggledNoUnusedVariablesRule ->
{ model | noUnusedVariablesEnabled = not model.noUnusedVariablesEnabled }
|> runReview
2019-07-05 00:57:55 +03:00
UserToggledNoUnusedTypeConstructorsRule ->
{ model | noUnusedTypeConstructorsEnabled = not model.noUnusedTypeConstructorsEnabled }
|> runReview
2019-07-05 00:57:55 +03:00
UserToggledConfigurationAsText ->
{ model | showConfigurationAsText = not model.showConfigurationAsText }
runReview : Model -> Model
runReview model =
{ model | reviewErrors = Review.review (config model) Project.new (file 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"
]
[ viewReviewErrors 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 "Review 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
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
2019-08-29 15:37:22 +03:00
, { import_ = "NoDebug"
, configExpression = "NoDebug.rule"
}
)
, ( model.noUnusedVariablesEnabled
2019-08-29 15:37:22 +03:00
, { import_ = "NoUnused.Variables"
, configExpression = "NoUnused.Variables.rule"
}
)
2019-07-05 00:57:55 +03:00
, ( model.noUnusedTypeConstructorsEnabled
2019-08-29 15:37:22 +03:00
, { import_ = "NoUnused.CustomTypeConstructors"
, configExpression = "NoUnused.CustomTypeConstructors.rule"
2019-07-05 00:57:55 +03:00
}
)
]
|> 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 ReviewConfig exposing (config)
import Review.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
]
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 =
if List.isEmpty model.reviewErrors then
[ { str = "I found no linting errors.\nYou're all good!"
, color = Nothing
, backgroundColor = Nothing
}
]
else
[ ( file model.sourceCode
, model.reviewErrors
|> List.map fromReviewError
)
]
|> Reporter.formatReport Reporter.Reviewing
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 }