2017-01-07 20:29:26 +03:00
|
|
|
module Main exposing (main)
|
|
|
|
|
2018-11-05 21:06:03 +03:00
|
|
|
import Browser
|
2017-06-12 21:04:02 +03:00
|
|
|
import Html exposing (..)
|
2019-07-03 15:45:05 +03:00
|
|
|
import Html.Attributes as Attr
|
|
|
|
import Html.Events as Events
|
2019-07-03 14:27:33 +03:00
|
|
|
import Lint exposing (LintError, Severity(..), lintSource)
|
2019-06-24 23:55:16 +03:00
|
|
|
import Lint.Rule exposing (Rule)
|
2019-07-03 15:45:05 +03:00
|
|
|
import Lint.Rule.DefaultPatternPosition as DefaultPatternPosition exposing (PatternPosition)
|
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
|
2018-11-22 21:19:19 +03:00
|
|
|
import Lint.Rule.NoUnusedVariables
|
2017-01-07 20:29:26 +03:00
|
|
|
|
|
|
|
|
2019-06-24 02:20:10 +03:00
|
|
|
|
|
|
|
-- LINT CONFIGURATION
|
2017-01-07 20:29:26 +03:00
|
|
|
|
|
|
|
|
2019-07-03 15:45:05 +03:00
|
|
|
config : Model -> List ( Severity, Rule )
|
|
|
|
config model =
|
|
|
|
[ ( model.noDebugEnabled, ( Critical, Lint.Rule.NoDebug.rule ) )
|
|
|
|
, ( model.noUnusedVariablesEnabled, ( Critical, Lint.Rule.NoUnusedVariables.rule ) )
|
|
|
|
, ( model.noImportingEverythingEnabled, ( Critical, Lint.Rule.NoImportingEverything.rule { exceptions = [ "Html" ] } ) )
|
|
|
|
, ( model.defaultPatternPositionEnabled, ( Critical, DefaultPatternPosition.rule model.defaultPatternPositionPattern ) )
|
|
|
|
, ( model.noExtraBooleanComparisonEnabled, ( Critical, Lint.Rule.NoExtraBooleanComparison.rule ) )
|
2018-11-11 01:43:58 +03:00
|
|
|
|
|
|
|
-- , ( Critical, Lint.Rule.NoConstantCondition.rule )
|
|
|
|
-- , ( Critical, Lint.Rule.NoDuplicateImports.rule )
|
|
|
|
-- , ( Critical, Lint.Rule.NoExposingEverything.rule )
|
|
|
|
-- , ( Critical, Lint.Rule.NoNestedLet.rule )
|
|
|
|
-- , ( Critical, Lint.Rule.NoUnannotatedFunction.rule )
|
|
|
|
-- , ( Critical, Lint.Rule.NoUselessIf.rule )
|
|
|
|
-- , ( Critical, Lint.Rule.NoUselessPatternMatching.rule )
|
|
|
|
-- , ( Warning, Lint.Rule.NoWarningComments.rule )
|
|
|
|
-- , ( Critical, Lint.Rule.SimplifyPiping.rule )
|
|
|
|
-- , ( Critical, Lint.Rule.SimplifyPropertyAccess.rule )
|
|
|
|
-- , ( Critical, Lint.Rule.ElmTest.NoDuplicateTestBodies.rule )
|
2017-01-20 01:31:55 +03:00
|
|
|
]
|
2019-07-03 19:11:38 +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
|
2019-07-03 14:27:33 +03:00
|
|
|
, lintResult : Result (List String) (List ( Severity, LintError ))
|
2019-07-03 15:45:05 +03:00
|
|
|
, noDebugEnabled : Bool
|
|
|
|
, noUnusedVariablesEnabled : Bool
|
|
|
|
, noImportingEverythingEnabled : Bool
|
2019-07-03 19:26:53 +03:00
|
|
|
, noImportingEverythingExceptions : List String
|
2019-07-03 15:45:05 +03:00
|
|
|
, defaultPatternPositionEnabled : Bool
|
|
|
|
, defaultPatternPositionPattern : PatternPosition
|
|
|
|
, noExtraBooleanComparisonEnabled : Bool
|
2019-07-03 19:11:38 +03:00
|
|
|
, showConfigurationAsText : Bool
|
2019-06-24 02:20:10 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-06-24 02:00:22 +03:00
|
|
|
init : Model
|
2017-01-07 20:29:26 +03:00
|
|
|
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)
|
2017-01-07 20:29:26 +03:00
|
|
|
|
|
|
|
f : Int -> Int
|
2017-01-07 23:17:01 +03:00
|
|
|
f x = x Debug.log 1
|
2017-01-07 20:29:26 +03:00
|
|
|
|
2017-01-19 23:02:21 +03:00
|
|
|
g n = n + 1
|
2017-01-07 20:29:26 +03:00
|
|
|
"""
|
2019-07-03 15:45:05 +03:00
|
|
|
|
|
|
|
tmpModel : Model
|
|
|
|
tmpModel =
|
|
|
|
{ sourceCode = sourceCode
|
|
|
|
, lintResult = Result.Ok []
|
|
|
|
, noDebugEnabled = True
|
|
|
|
, noUnusedVariablesEnabled = True
|
|
|
|
, noImportingEverythingEnabled = True
|
2019-07-03 19:26:53 +03:00
|
|
|
, noImportingEverythingExceptions = [ "Html", "Html.Attributes" ]
|
2019-07-03 15:45:05 +03:00
|
|
|
, defaultPatternPositionEnabled = True
|
|
|
|
, defaultPatternPositionPattern = DefaultPatternPosition.ShouldBeLast
|
|
|
|
, noExtraBooleanComparisonEnabled = True
|
2019-07-03 19:11:38 +03:00
|
|
|
, showConfigurationAsText = False
|
2019-07-03 15:45:05 +03:00
|
|
|
}
|
2019-06-24 02:00:22 +03:00
|
|
|
in
|
2019-07-03 15:45:05 +03:00
|
|
|
{ tmpModel | lintResult = lintSource (config tmpModel) sourceCode }
|
2019-06-24 02:00:22 +03:00
|
|
|
|
2017-01-07 20:29:26 +03:00
|
|
|
|
2019-06-24 02:20:10 +03:00
|
|
|
|
|
|
|
-- UPDATE
|
|
|
|
|
|
|
|
|
|
|
|
type Msg
|
|
|
|
= UserEditedSourceCode String
|
2019-07-03 15:45:05 +03:00
|
|
|
| UserToggledNoDebugRule
|
|
|
|
| UserToggledNoUnusedVariablesRule
|
|
|
|
| UserToggledNoImportingEverythingRule
|
|
|
|
| UserToggledDefaultPatternPositionRule
|
|
|
|
| UserToggledNoExtraBooleanComparisonRule
|
|
|
|
| UserChangedDefaultPatternSetting PatternPosition
|
2019-07-03 19:11:38 +03:00
|
|
|
| UserToggledConfigurationAsText
|
2017-01-07 20:29:26 +03:00
|
|
|
|
2019-06-24 02:00:22 +03:00
|
|
|
|
|
|
|
update : Msg -> Model -> Model
|
2017-01-07 20:29:26 +03:00
|
|
|
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
|
2019-07-03 15:45:05 +03:00
|
|
|
, lintResult = lintSource (config model) sourceCode
|
2019-06-24 02:00:22 +03:00
|
|
|
}
|
2017-01-07 20:29:26 +03:00
|
|
|
|
2019-07-03 15:45:05 +03:00
|
|
|
UserToggledNoDebugRule ->
|
|
|
|
{ model | noDebugEnabled = not model.noDebugEnabled }
|
|
|
|
|> rerunLinting
|
|
|
|
|
|
|
|
UserToggledNoUnusedVariablesRule ->
|
|
|
|
{ model | noUnusedVariablesEnabled = not model.noUnusedVariablesEnabled }
|
|
|
|
|> rerunLinting
|
|
|
|
|
|
|
|
UserToggledNoImportingEverythingRule ->
|
|
|
|
{ model | noImportingEverythingEnabled = not model.noImportingEverythingEnabled }
|
|
|
|
|> rerunLinting
|
|
|
|
|
|
|
|
UserToggledDefaultPatternPositionRule ->
|
|
|
|
{ model | defaultPatternPositionEnabled = not model.defaultPatternPositionEnabled }
|
|
|
|
|> rerunLinting
|
|
|
|
|
|
|
|
UserChangedDefaultPatternSetting patternPosition ->
|
|
|
|
{ model | defaultPatternPositionPattern = patternPosition }
|
|
|
|
|> rerunLinting
|
|
|
|
|
|
|
|
UserToggledNoExtraBooleanComparisonRule ->
|
|
|
|
{ model | noExtraBooleanComparisonEnabled = not model.noExtraBooleanComparisonEnabled }
|
|
|
|
|> rerunLinting
|
|
|
|
|
2019-07-03 19:11:38 +03:00
|
|
|
UserToggledConfigurationAsText ->
|
|
|
|
{ model | showConfigurationAsText = not model.showConfigurationAsText }
|
|
|
|
|
2019-07-03 15:45:05 +03:00
|
|
|
|
|
|
|
rerunLinting : Model -> Model
|
|
|
|
rerunLinting model =
|
|
|
|
{ model | lintResult = lintSource (config model) model.sourceCode }
|
|
|
|
|
2017-01-07 20:29:26 +03:00
|
|
|
|
2018-11-06 15:15:23 +03:00
|
|
|
|
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
|
2017-01-07 20:29:26 +03:00
|
|
|
view model =
|
2019-07-03 15:45:05 +03:00
|
|
|
div [ Attr.id "wrapper" ]
|
|
|
|
[ div [ Attr.id "left" ]
|
|
|
|
[ p [ Attr.class "title" ] [ text "Source code" ]
|
2019-06-24 02:11:18 +03:00
|
|
|
, div
|
2019-07-03 15:45:05 +03:00
|
|
|
[ Attr.style "display" "flex"
|
|
|
|
, Attr.style "flex-direction" "row"
|
2017-01-16 00:57:03 +03:00
|
|
|
]
|
2019-06-24 02:11:18 +03:00
|
|
|
[ textarea
|
2019-07-03 15:45:05 +03:00
|
|
|
[ Attr.id "input"
|
|
|
|
, Events.onInput UserEditedSourceCode
|
|
|
|
, Attr.style "height" "500px"
|
|
|
|
, Attr.style "width" "60%"
|
2019-06-24 02:11:18 +03:00
|
|
|
]
|
|
|
|
[ text model.sourceCode ]
|
2019-07-03 15:45:05 +03:00
|
|
|
, div [ Attr.style "margin-left" "2rem" ]
|
|
|
|
[ viewConfigurationPanel model
|
2019-07-03 19:11:38 +03:00
|
|
|
, viewConfigurationAsText model
|
2019-07-03 15:45:05 +03:00
|
|
|
, p [ Attr.class "title" ] [ text "Linting errors" ]
|
|
|
|
, ul [ Attr.id "lint" ]
|
2019-06-24 02:11:18 +03:00
|
|
|
(lintErrors model)
|
|
|
|
]
|
2017-01-16 00:57:03 +03:00
|
|
|
]
|
2017-01-10 00:12:37 +03:00
|
|
|
]
|
|
|
|
]
|
2017-01-07 20:29:26 +03:00
|
|
|
|
|
|
|
|
2019-07-03 15:45:05 +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
|
|
|
|
, form [ Attr.action "" ]
|
|
|
|
[ viewCheckbox UserToggledDefaultPatternPositionRule "DefaultPatternPosition" model.defaultPatternPositionEnabled
|
|
|
|
, viewRadioButton
|
|
|
|
UserChangedDefaultPatternSetting
|
|
|
|
DefaultPatternPosition.ShouldBeLast
|
|
|
|
"Should be last"
|
|
|
|
model.defaultPatternPositionEnabled
|
|
|
|
model.defaultPatternPositionPattern
|
|
|
|
, viewRadioButton
|
|
|
|
UserChangedDefaultPatternSetting
|
|
|
|
DefaultPatternPosition.ShouldBeFirst
|
|
|
|
"Should be first"
|
|
|
|
model.defaultPatternPositionEnabled
|
|
|
|
model.defaultPatternPositionPattern
|
|
|
|
]
|
|
|
|
, viewCheckbox UserToggledNoExtraBooleanComparisonRule "NoExtraBooleanComparison" model.noExtraBooleanComparisonEnabled
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
|
2019-07-03 19:11:38 +03:00
|
|
|
viewConfigurationAsText : Model -> Html Msg
|
|
|
|
viewConfigurationAsText model =
|
|
|
|
if model.showConfigurationAsText then
|
|
|
|
div
|
|
|
|
[ Attr.style "display" "flex"
|
|
|
|
, Attr.style "flex-direction" "column"
|
|
|
|
]
|
|
|
|
[ 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.defaultPatternPositionEnabled
|
|
|
|
, { import_ = "Lint.Rule.DefaultPatternPosition as DefaultPatternPosition"
|
|
|
|
, configExpression =
|
|
|
|
"DefaultPatternPosition.rule DefaultPatternPosition."
|
|
|
|
++ (case model.defaultPatternPositionPattern of
|
|
|
|
DefaultPatternPosition.ShouldBeFirst ->
|
|
|
|
"ShouldBeFirst"
|
|
|
|
|
|
|
|
DefaultPatternPosition.ShouldBeLast ->
|
|
|
|
"ShouldBeLast"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
)
|
|
|
|
, ( model.noExtraBooleanComparisonEnabled
|
|
|
|
, { import_ = "Lint.Rule.NoExtraBooleanComparison"
|
|
|
|
, configExpression = "Lint.Rule.NoExtraBooleanComparison.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 } -> " ( Critical, " ++ configExpression ++ " )")
|
|
|
|
|> String.join "\n ,"
|
|
|
|
in
|
|
|
|
"""module LintConfig exposing (config)
|
|
|
|
|
|
|
|
import Lint exposing (Severity(..))
|
|
|
|
import Lint.Rule exposing (Rule)
|
|
|
|
""" ++ importStatements ++ """
|
|
|
|
|
|
|
|
|
|
|
|
config : List ( Severity, Rule )
|
|
|
|
config =
|
|
|
|
[""" ++ configExpressions ++ """
|
|
|
|
]
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
2019-07-03 15:45:05 +03:00
|
|
|
viewCheckbox : Msg -> String -> Bool -> Html Msg
|
|
|
|
viewCheckbox onClick name checked =
|
|
|
|
label
|
|
|
|
[]
|
|
|
|
[ input
|
|
|
|
[ Attr.type_ "checkbox"
|
|
|
|
, Attr.checked checked
|
|
|
|
, Events.onClick onClick
|
|
|
|
]
|
|
|
|
[]
|
|
|
|
, text name
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
viewRadioButton : (PatternPosition -> Msg) -> PatternPosition -> String -> Bool -> PatternPosition -> Html Msg
|
|
|
|
viewRadioButton onClick patternPosition name enabled selectedPatternPosition =
|
|
|
|
label
|
|
|
|
[]
|
|
|
|
[ input
|
|
|
|
[ Attr.type_ "radio"
|
|
|
|
, Attr.checked (patternPosition == selectedPatternPosition)
|
|
|
|
, Events.onClick (onClick patternPosition)
|
|
|
|
, Attr.disabled <| not enabled
|
|
|
|
, Attr.name name
|
|
|
|
, Attr.value name
|
|
|
|
]
|
|
|
|
[]
|
|
|
|
, text name
|
|
|
|
]
|
|
|
|
|
|
|
|
|
2019-06-24 02:20:10 +03:00
|
|
|
lintErrors : Model -> List (Html Msg)
|
|
|
|
lintErrors model =
|
|
|
|
let
|
|
|
|
messages : List String
|
|
|
|
messages =
|
|
|
|
case model.lintResult of
|
|
|
|
Err errors ->
|
|
|
|
errors
|
|
|
|
|
|
|
|
Ok errors ->
|
|
|
|
if List.isEmpty errors then
|
|
|
|
[ "No errors." ]
|
|
|
|
|
|
|
|
else
|
|
|
|
List.map (Tuple.second >> errorToString) errors
|
|
|
|
in
|
|
|
|
List.map
|
|
|
|
(\message -> li [] [ text message ])
|
|
|
|
messages
|
|
|
|
|
|
|
|
|
2019-07-03 14:27:33 +03:00
|
|
|
errorToString : LintError -> String
|
|
|
|
errorToString { ruleName, message, range } =
|
2019-06-24 02:20:10 +03:00
|
|
|
let
|
|
|
|
location : String
|
|
|
|
location =
|
|
|
|
"(line " ++ String.fromInt range.start.row ++ ", column " ++ String.fromInt range.start.column ++ ")"
|
|
|
|
in
|
2019-07-03 14:27:33 +03:00
|
|
|
ruleName ++ ": " ++ message ++ " " ++ location
|
2019-06-24 02:20:10 +03:00
|
|
|
|
|
|
|
|
2019-06-24 02:00:22 +03:00
|
|
|
main : Program () Model Msg
|
2017-01-07 20:29:26 +03:00
|
|
|
main =
|
2018-11-05 21:06:03 +03:00
|
|
|
Browser.sandbox
|
2018-11-05 21:30:47 +03:00
|
|
|
{ init = init
|
2017-01-07 20:29:26 +03:00
|
|
|
, update = update
|
|
|
|
, view = view
|
|
|
|
}
|