mirror of
https://github.com/natefaubion/purescript-tidy.git
synced 2024-12-01 20:53:34 +03:00
Add option to sort imports like purs-ide (#71)
* Add option to sort imports like purs-ide * Add dep
This commit is contained in:
parent
b6ed20df8c
commit
75836c7d5c
@ -13,10 +13,11 @@ import Data.Argonaut.Encode (assoc, encodeJson, extend)
|
||||
import Data.Either (Either)
|
||||
import Data.Maybe (Maybe(..), fromMaybe, maybe)
|
||||
import Data.Traversable (traverse)
|
||||
import Tidy (ImportWrapOption(..), TypeArrowOption(..), UnicodeOption(..))
|
||||
import Tidy (ImportSortOption(..), ImportWrapOption(..), TypeArrowOption(..), UnicodeOption(..))
|
||||
|
||||
type FormatOptions =
|
||||
{ importWrap :: ImportWrapOption
|
||||
{ importSort :: ImportSortOption
|
||||
, importWrap :: ImportWrapOption
|
||||
, indent :: Int
|
||||
, operatorsFile :: Maybe String
|
||||
, ribbon :: Number
|
||||
@ -27,7 +28,8 @@ type FormatOptions =
|
||||
|
||||
defaults :: FormatOptions
|
||||
defaults =
|
||||
{ importWrap: ImportWrapSource
|
||||
{ importSort: ImportSortSource
|
||||
, importWrap: ImportWrapSource
|
||||
, indent: 2
|
||||
, operatorsFile: Nothing
|
||||
, ribbon: 1.0
|
||||
@ -39,7 +41,17 @@ defaults =
|
||||
formatOptions :: ArgParser FormatOptions
|
||||
formatOptions =
|
||||
Arg.fromRecord
|
||||
{ importWrap:
|
||||
{ importSort:
|
||||
Arg.choose "import sort"
|
||||
[ Arg.flag [ "--import-sort-source", "-iss" ]
|
||||
"Imports are not automatically sorted and keep the same order as in the source.\nDefault."
|
||||
$> ImportSortSource
|
||||
, Arg.flag [ "--import-sort-ide", "-isi" ]
|
||||
"Imports are automatically sorted like purs-ide."
|
||||
$> ImportSortIde
|
||||
]
|
||||
# Arg.default defaults.importSort
|
||||
, importWrap:
|
||||
Arg.choose "import wrap"
|
||||
[ Arg.flag [ "--import-wrap-source", "-iws" ]
|
||||
"Imports are wrapped only when breaks are in the source.\nDefault. Works well with IDE imports."
|
||||
@ -100,6 +112,7 @@ unicodeOption =
|
||||
fromJson :: Json -> Either JsonDecodeError FormatOptions
|
||||
fromJson json = do
|
||||
obj <- decodeJson json
|
||||
importSort <- traverse importSortFromString =<< obj .:? "importSort"
|
||||
importWrap <- traverse importWrapFromString =<< obj .:? "importWrap"
|
||||
indent <- obj .:? "indent"
|
||||
operatorsFile <- obj .:? "operatorsFile"
|
||||
@ -108,7 +121,8 @@ fromJson json = do
|
||||
unicode <- traverse unicodeFromString =<< obj .:? "unicode"
|
||||
width <- obj .:? "width"
|
||||
pure
|
||||
{ importWrap: fromMaybe defaults.importWrap importWrap
|
||||
{ importSort: fromMaybe defaults.importSort importSort
|
||||
, importWrap: fromMaybe defaults.importWrap importWrap
|
||||
, indent: fromMaybe defaults.indent indent
|
||||
, operatorsFile: operatorsFile <|> defaults.operatorsFile
|
||||
, ribbon: fromMaybe defaults.ribbon ribbon
|
||||
@ -162,3 +176,14 @@ importWrapToString :: ImportWrapOption -> String
|
||||
importWrapToString = case _ of
|
||||
ImportWrapSource -> "source"
|
||||
ImportWrapAuto -> "auto"
|
||||
|
||||
importSortFromString :: String -> Either JsonDecodeError ImportSortOption
|
||||
importSortFromString = case _ of
|
||||
"source" -> pure ImportSortSource
|
||||
"ide" -> pure ImportSortIde
|
||||
other -> throwError $ UnexpectedValue (Json.fromString other)
|
||||
|
||||
importSortToString :: ImportSortOption -> String
|
||||
importSortToString = case _ of
|
||||
ImportSortSource -> "source"
|
||||
ImportSortIde -> "ide"
|
||||
|
@ -30,7 +30,8 @@ import Tidy.Operators (parseOperatorTable)
|
||||
import Tidy.Precedence (PrecedenceMap, remapOperators)
|
||||
|
||||
type WorkerConfig =
|
||||
{ importWrap :: String
|
||||
{ importSort :: String
|
||||
, importWrap :: String
|
||||
, indent :: Int
|
||||
, operatorsFile :: String
|
||||
, ribbon :: Number
|
||||
@ -41,7 +42,8 @@ type WorkerConfig =
|
||||
|
||||
toWorkerConfig :: FormatOptions -> WorkerConfig
|
||||
toWorkerConfig options =
|
||||
{ importWrap: FormatOptions.importWrapToString options.importWrap
|
||||
{ importSort: FormatOptions.importSortToString options.importSort
|
||||
, importWrap: FormatOptions.importWrapToString options.importWrap
|
||||
, indent: options.indent
|
||||
, operatorsFile: fromMaybe ".tidyoperators.default" options.operatorsFile
|
||||
, ribbon: options.ribbon
|
||||
@ -105,7 +107,10 @@ formatInPlaceCommand shouldCheck operators { filePath, config } = do
|
||||
let
|
||||
formatOptions :: FormatOptions
|
||||
formatOptions =
|
||||
{ importWrap:
|
||||
{ importSort:
|
||||
fromRight' (\_ -> unsafeCrashWith "Unknown importSort value") do
|
||||
FormatOptions.importSortFromString config.importSort
|
||||
, importWrap:
|
||||
fromRight' (\_ -> unsafeCrashWith "Unknown importWrap value") do
|
||||
FormatOptions.importWrapFromString config.importWrap
|
||||
, indent: config.indent
|
||||
|
@ -27,6 +27,7 @@
|
||||
, "node-workerbees"
|
||||
, "numbers"
|
||||
, "ordered-collections"
|
||||
, "orders"
|
||||
, "parallel"
|
||||
, "partial"
|
||||
, "prelude"
|
||||
|
@ -8,6 +8,7 @@
|
||||
, "lists"
|
||||
, "maybe"
|
||||
, "ordered-collections"
|
||||
, "orders"
|
||||
, "partial"
|
||||
, "prelude"
|
||||
, "purescript-language-cst-parser"
|
||||
|
127
src/Tidy.purs
127
src/Tidy.purs
@ -2,6 +2,7 @@ module Tidy
|
||||
( FormatOptions
|
||||
, defaultFormatOptions
|
||||
, TypeArrowOption(..)
|
||||
, ImportSortOption(..)
|
||||
, ImportWrapOption(..)
|
||||
, Format
|
||||
, formatModule
|
||||
@ -26,14 +27,17 @@ import Data.Map as Map
|
||||
import Data.Maybe (Maybe(..), maybe)
|
||||
import Data.Monoid (power)
|
||||
import Data.Monoid as Monoid
|
||||
import Data.Newtype (un)
|
||||
import Data.Ord.Down (Down(..))
|
||||
import Data.String.CodeUnits as SCU
|
||||
import Data.Tuple (Tuple(..), fst)
|
||||
import Data.Tuple (Tuple(..), fst, snd)
|
||||
import Dodo as Dodo
|
||||
import Partial.Unsafe (unsafeCrashWith)
|
||||
import PureScript.CST.Errors (RecoveredError(..))
|
||||
import PureScript.CST.Types (Binder(..), ClassFundep(..), ClassHead, Comment(..), DataCtor(..), DataHead, DataMembers(..), Declaration(..), Delimited, DelimitedNonEmpty, DoStatement(..), Export(..), Expr(..), FixityOp(..), Foreign(..), Guarded(..), GuardedExpr(..), Ident, IfThenElse, Import(..), ImportDecl(..), Instance(..), InstanceBinding(..), InstanceHead, Label, Labeled(..), LetBinding(..), LineFeed, Module(..), ModuleBody(..), ModuleHeader(..), Name(..), OneOrDelimited(..), Operator, PatternGuard(..), Proper, QualifiedName(..), RecordLabeled(..), RecordUpdate(..), Row(..), Separated(..), SourceStyle(..), SourceToken, Token(..), Type(..), TypeVarBinding(..), ValueBindingFields, Where(..), Wrapped(..))
|
||||
import PureScript.CST.Types (Binder(..), ClassFundep(..), ClassHead, Comment(..), DataCtor(..), DataHead, DataMembers(..), Declaration(..), Delimited, DelimitedNonEmpty, DoStatement(..), Export(..), Expr(..), FixityOp(..), Foreign(..), Guarded(..), GuardedExpr(..), Ident, IfThenElse, Import(..), ImportDecl(..), Instance(..), InstanceBinding(..), InstanceHead, Label, Labeled(..), LetBinding(..), LineFeed, Module(..), ModuleBody(..), ModuleHeader(..), ModuleName, Name(..), OneOrDelimited(..), Operator, PatternGuard(..), Proper, QualifiedName(..), RecordLabeled(..), RecordUpdate(..), Row(..), Separated(..), SourceStyle(..), SourceToken, Token(..), Type(..), TypeVarBinding(..), ValueBindingFields, Where(..), Wrapped(..))
|
||||
import Tidy.Doc (FormatDoc, align, alignCurrentColumn, anchor, break, flattenMax, flexDoubleBreak, flexGroup, flexSoftBreak, flexSpaceBreak, forceMinSourceBreaks, fromDoc, indent, joinWith, joinWithMap, leadingBlockComment, leadingLineComment, locally, softBreak, softSpace, sourceBreak, space, spaceBreak, text, trailingBlockComment, trailingLineComment)
|
||||
import Tidy.Doc (FormatDoc, toDoc) as Exports
|
||||
import Tidy.Doc as Doc
|
||||
import Tidy.Hang (HangingDoc, HangingOp(..), hang, hangApp, hangBreak, hangOps, hangWithIndent)
|
||||
import Tidy.Hang as Hang
|
||||
import Tidy.Precedence (OperatorNamespace(..), OperatorTree(..), PrecedenceMap, QualifiedOperator(..), toOperatorTree)
|
||||
@ -53,11 +57,18 @@ data ImportWrapOption
|
||||
|
||||
derive instance eqImportWrapOption :: Eq ImportWrapOption
|
||||
|
||||
data ImportSortOption
|
||||
= ImportSortSource
|
||||
| ImportSortIde
|
||||
|
||||
derive instance eqImportSortOpion :: Eq ImportSortOption
|
||||
|
||||
type FormatOptions e a =
|
||||
{ formatError :: e -> FormatDoc a
|
||||
, unicode :: UnicodeOption
|
||||
, typeArrowPlacement :: TypeArrowOption
|
||||
, operators :: PrecedenceMap
|
||||
, importSort :: ImportSortOption
|
||||
, importWrap :: ImportWrapOption
|
||||
}
|
||||
|
||||
@ -67,6 +78,7 @@ defaultFormatOptions =
|
||||
, unicode: UnicodeSource
|
||||
, typeArrowPlacement: TypeArrowFirst
|
||||
, operators: Map.empty
|
||||
, importSort: ImportSortSource
|
||||
, importWrap: ImportWrapSource
|
||||
}
|
||||
|
||||
@ -203,8 +215,55 @@ formatModule conf (Module { header: ModuleHeader header, body: ModuleBody body }
|
||||
, foldr (formatComment leadingLineComment leadingBlockComment) mempty body.trailingComments
|
||||
]
|
||||
where
|
||||
formatImports k =
|
||||
joinWithMap break (k <<< formatImportDecl conf)
|
||||
|
||||
imports =
|
||||
joinWithMap break (formatImportDecl conf) header.imports
|
||||
case conf.importSort of
|
||||
ImportSortSource ->
|
||||
formatImports identity header.imports
|
||||
ImportSortIde -> do
|
||||
let { init, rest } = Array.span importOpen sorted
|
||||
formatImports Doc.flatten init
|
||||
<> forceMinSourceBreaks 2 (formatImports Doc.flatten rest)
|
||||
where
|
||||
sorted =
|
||||
Array.sortBy
|
||||
( comparing (Down <<< importOpen)
|
||||
<> comparing importModuleName
|
||||
<> comparing importQualified
|
||||
)
|
||||
header.imports
|
||||
|
||||
importOpen (ImportDecl a) = case a.qualified, a.names of
|
||||
Nothing, Nothing ->
|
||||
true
|
||||
Nothing, Just (Tuple (Just _) _) ->
|
||||
true
|
||||
_, _ ->
|
||||
false
|
||||
|
||||
importModuleName (ImportDecl { module: Name { name } }) =
|
||||
name
|
||||
|
||||
importQualified (ImportDecl a) = case a.qualified of
|
||||
Nothing ->
|
||||
ImportExplicit Nothing
|
||||
Just (Tuple _ (Name { name })) ->
|
||||
case a.names of
|
||||
Just (Tuple Nothing _) ->
|
||||
ImportExplicit (Just name)
|
||||
Just (Tuple (Just _) _) ->
|
||||
ImportHiding name
|
||||
_ -> ImportAll name
|
||||
|
||||
data ImportModuleComparison
|
||||
= ImportExplicit (Maybe ModuleName)
|
||||
| ImportAll ModuleName
|
||||
| ImportHiding ModuleName
|
||||
|
||||
derive instance eqImportModuleComparison :: Eq ImportModuleComparison
|
||||
derive instance ordImportModuleComparison :: Ord ImportModuleComparison
|
||||
|
||||
formatExport :: forall e a. Format (Export e) e a
|
||||
formatExport conf = case _ of
|
||||
@ -240,11 +299,11 @@ formatImportDecl conf (ImportDecl imp) =
|
||||
Just (Tuple (Just hiding) nameList) ->
|
||||
formatName conf imp."module"
|
||||
`space` anchor (formatToken conf hiding)
|
||||
`flexSpaceBreak` anchor (formatParenListNonEmpty NotGrouped formatImport conf nameList)
|
||||
`flexSpaceBreak` anchor (formatParenListNonEmpty NotGrouped formatImport conf (sortImports nameList))
|
||||
`space` anchor (foldMap formatImportQualified imp.qualified)
|
||||
Just (Tuple Nothing nameList) ->
|
||||
formatName conf imp."module"
|
||||
`flexSpaceBreak` anchor (formatParenListNonEmpty NotGrouped formatImport conf nameList)
|
||||
`flexSpaceBreak` anchor (formatParenListNonEmpty NotGrouped formatImport conf (sortImports nameList))
|
||||
`space` anchor (foldMap formatImportQualified imp.qualified)
|
||||
Nothing ->
|
||||
formatName conf imp."module"
|
||||
@ -253,6 +312,64 @@ formatImportDecl conf (ImportDecl imp) =
|
||||
formatImportQualified (Tuple as qualName) =
|
||||
formatToken conf as `space` anchor (formatName conf qualName)
|
||||
|
||||
sortImports imports@(Wrapped { open, value: Separated { head, tail }, close }) = case conf.importSort of
|
||||
ImportSortSource ->
|
||||
imports
|
||||
ImportSortIde ->
|
||||
Wrapped
|
||||
{ open
|
||||
, value: Separated
|
||||
{ head: sorted.head
|
||||
, tail: Array.zip commas sorted.tail
|
||||
}
|
||||
, close
|
||||
}
|
||||
where
|
||||
Tuple commas tail' =
|
||||
Array.unzip tail
|
||||
|
||||
sorted =
|
||||
NonEmptyArray.cons' head tail'
|
||||
# NonEmptyArray.sortBy (comparing toComparison)
|
||||
# NonEmptyArray.uncons
|
||||
|
||||
toComparison = case _ of
|
||||
ImportValue (Name { name }) ->
|
||||
ImportValueCmp name
|
||||
ImportOp (Name { name }) ->
|
||||
ImportOpCmp name
|
||||
ImportType (Name { name }) Nothing ->
|
||||
ImportTypeCmp name
|
||||
ImportType (Name { name }) (Just (DataEnumerated (Wrapped { value }))) ->
|
||||
case value of
|
||||
Nothing ->
|
||||
ImportTypeEnumeratedCmp name []
|
||||
Just (Separated ctors) ->
|
||||
ImportTypeEnumeratedCmp name $ (_.name <<< un Name) <$> Array.cons ctors.head (map snd ctors.tail)
|
||||
ImportType (Name { name }) (Just (DataAll _)) ->
|
||||
ImportTypeAllCmp name
|
||||
ImportTypeOp _ (Name { name }) ->
|
||||
ImportTypeOpCmp name
|
||||
ImportClass _ (Name { name }) ->
|
||||
ImportClassCmp name
|
||||
ImportKind _ (Name { name }) ->
|
||||
ImportTypeCmp name
|
||||
ImportError _ ->
|
||||
ImportErrorCmp
|
||||
|
||||
data ImportComparison
|
||||
= ImportClassCmp Proper
|
||||
| ImportTypeOpCmp Operator
|
||||
| ImportTypeAllCmp Proper
|
||||
| ImportTypeCmp Proper
|
||||
| ImportTypeEnumeratedCmp Proper (Array Proper)
|
||||
| ImportValueCmp Ident
|
||||
| ImportOpCmp Operator
|
||||
| ImportErrorCmp
|
||||
|
||||
derive instance eqImportComparison :: Eq ImportComparison
|
||||
derive instance ordImportComparison :: Ord ImportComparison
|
||||
|
||||
formatImport :: forall e a. Format (Import e) e a
|
||||
formatImport conf = case _ of
|
||||
ImportValue n ->
|
||||
|
@ -112,6 +112,7 @@ parseDirectivesFromModule (Module { header: ModuleHeader header, body }) =
|
||||
, operators: default.operators
|
||||
, unicode: opts.unicode
|
||||
, typeArrowPlacement: opts.typeArrowPlacement
|
||||
, importSort: opts.importSort
|
||||
, importWrap: opts.importWrap
|
||||
}
|
||||
}
|
||||
|
38
test/snapshots/ImportSortIde.output
Normal file
38
test/snapshots/ImportSortIde.output
Normal file
@ -0,0 +1,38 @@
|
||||
module ImportSortIde where
|
||||
|
||||
import Prim hiding (Type, Row)
|
||||
|
||||
import Data.Array (sortBy) as Z
|
||||
import Data.Array (sortBy) as Array
|
||||
import Data.Array (sortBy) as A
|
||||
import Data.Array (sortBy)
|
||||
|
||||
import Data.Array hiding (sortBy) as Z
|
||||
import Data.Array hiding (sortBy) as Array
|
||||
import Data.Array hiding (sortBy) as A
|
||||
|
||||
import Data.Array as Z
|
||||
import Data.Array as Array
|
||||
import Data.Array as A
|
||||
|
||||
import Example (Test, type (+), Test(BBB, AAA), Test(..), Test(), test, class Test)
|
||||
|
||||
import Prelude
|
||||
|
||||
-- @format --import-sort-ide
|
||||
module ImportSortIde where
|
||||
|
||||
import Prelude
|
||||
import Prim hiding (Row, Type)
|
||||
|
||||
import Data.Array (sortBy)
|
||||
import Data.Array (sortBy) as A
|
||||
import Data.Array (sortBy) as Array
|
||||
import Data.Array (sortBy) as Z
|
||||
import Data.Array as A
|
||||
import Data.Array as Array
|
||||
import Data.Array as Z
|
||||
import Data.Array hiding (sortBy) as A
|
||||
import Data.Array hiding (sortBy) as Array
|
||||
import Data.Array hiding (sortBy) as Z
|
||||
import Example (class Test, type (+), Test(..), Test, Test(), Test(BBB, AAA), test)
|
21
test/snapshots/ImportSortIde.purs
Normal file
21
test/snapshots/ImportSortIde.purs
Normal file
@ -0,0 +1,21 @@
|
||||
-- @format --import-sort-ide
|
||||
module ImportSortIde where
|
||||
|
||||
import Prim hiding (Type, Row)
|
||||
|
||||
import Data.Array (sortBy) as Z
|
||||
import Data.Array (sortBy) as Array
|
||||
import Data.Array (sortBy) as A
|
||||
import Data.Array (sortBy)
|
||||
|
||||
import Data.Array hiding (sortBy) as Z
|
||||
import Data.Array hiding (sortBy) as Array
|
||||
import Data.Array hiding (sortBy) as A
|
||||
|
||||
import Data.Array as Z
|
||||
import Data.Array as Array
|
||||
import Data.Array as A
|
||||
|
||||
import Example (Test, type (+), Test(BBB, AAA), Test(..), Test(), test, class Test)
|
||||
|
||||
import Prelude
|
@ -27,6 +27,7 @@
|
||||
, "node-process"
|
||||
, "node-workerbees"
|
||||
, "ordered-collections"
|
||||
, "orders"
|
||||
, "partial"
|
||||
, "posix-types"
|
||||
, "prelude"
|
||||
|
Loading…
Reference in New Issue
Block a user