{-# LANGUAGE OverloadedStrings #-}
module Ormolu.Printer.Comments
( spitPrecedingComments,
spitFollowingComments,
spitRemainingComments,
spitStackHeader,
)
where
import Control.Monad
import Data.Char (isSpace)
import Data.Coerce (coerce)
import Data.Data (Data)
import Data.List (isPrefixOf)
import qualified Data.List.NonEmpty as NE
import Data.List.NonEmpty (NonEmpty (..))
import qualified Data.Text as T
import Ormolu.Parser.CommentStream
import Ormolu.Printer.Internal
import Ormolu.Utils (isModule)
import SrcLoc
spitPrecedingComments ::
Data a =>
RealLocated a ->
R ()
ref :: RealLocated a
ref = do
Bool
gotSome <- (Maybe RealSrcSpan -> R Bool) -> R Bool
handleCommentSeries (RealLocated a -> Maybe RealSrcSpan -> R Bool
forall a. Data a => RealLocated a -> Maybe RealSrcSpan -> R Bool
spitPrecedingComment RealLocated a
ref)
Bool -> R () -> R ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
gotSome (R () -> R ()) -> R () -> R ()
forall a b. (a -> b) -> a -> b
$ do
Maybe RealSrcSpan
lastSpn <- ((Maybe HaddockStyle, RealSrcSpan) -> RealSrcSpan)
-> Maybe (Maybe HaddockStyle, RealSrcSpan) -> Maybe RealSrcSpan
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Maybe HaddockStyle, RealSrcSpan) -> RealSrcSpan
forall a b. (a, b) -> b
snd (Maybe (Maybe HaddockStyle, RealSrcSpan) -> Maybe RealSrcSpan)
-> R (Maybe (Maybe HaddockStyle, RealSrcSpan))
-> R (Maybe RealSrcSpan)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> R (Maybe (Maybe HaddockStyle, RealSrcSpan))
getLastCommentSpan
Bool -> R () -> R ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (RealSrcSpan -> Maybe RealSrcSpan -> Bool
needsNewlineBefore (RealLocated a -> RealSrcSpan
forall a. RealLocated a -> RealSrcSpan
getRealSrcSpan RealLocated a
ref) Maybe RealSrcSpan
lastSpn) R ()
newline
spitFollowingComments ::
Data a =>
RealLocated a ->
R ()
ref :: RealLocated a
ref = do
RealSrcSpan -> R ()
trimSpanStream (RealLocated a -> RealSrcSpan
forall a. RealLocated a -> RealSrcSpan
getRealSrcSpan RealLocated a
ref)
R Bool -> R ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (R Bool -> R ()) -> R Bool -> R ()
forall a b. (a -> b) -> a -> b
$ (Maybe RealSrcSpan -> R Bool) -> R Bool
handleCommentSeries (RealLocated a -> Maybe RealSrcSpan -> R Bool
forall a. Data a => RealLocated a -> Maybe RealSrcSpan -> R Bool
spitFollowingComment RealLocated a
ref)
spitRemainingComments :: R ()
spitRemainingComments :: R ()
spitRemainingComments = R Bool -> R ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (R Bool -> R ()) -> R Bool -> R ()
forall a b. (a -> b) -> a -> b
$ (Maybe RealSrcSpan -> R Bool) -> R Bool
handleCommentSeries Maybe RealSrcSpan -> R Bool
spitRemainingComment
spitStackHeader :: R ()
= do
let isStackHeader :: Comment -> Bool
isStackHeader (Comment (x :: String
x :| _)) =
"stack" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
dropWhile Char -> Bool
isSpace (Int -> String -> String
forall a. Int -> [a] -> [a]
drop 2 String
x)
Maybe (RealLocated Comment)
mstackHeader <- (RealLocated Comment -> Bool) -> R (Maybe (RealLocated Comment))
popComment (Comment -> Bool
isStackHeader (Comment -> Bool)
-> (RealLocated Comment -> Comment) -> RealLocated Comment -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RealLocated Comment -> Comment
forall a. RealLocated a -> a
unRealSrcSpan)
Maybe (RealLocated Comment)
-> (RealLocated Comment -> R ()) -> R ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ Maybe (RealLocated Comment)
mstackHeader ((RealLocated Comment -> R ()) -> R ())
-> (RealLocated Comment -> R ()) -> R ()
forall a b. (a -> b) -> a -> b
$ \(L spn :: RealSrcSpan
spn x :: Comment
x) -> do
RealSrcSpan -> Comment -> R ()
spitCommentNow RealSrcSpan
spn Comment
x
R ()
newline
spitPrecedingComment ::
Data a =>
RealLocated a ->
Maybe RealSrcSpan ->
R Bool
(L ref :: RealSrcSpan
ref a :: a
a) mlastSpn :: Maybe RealSrcSpan
mlastSpn = do
let p :: GenLocated RealSrcSpan e -> Bool
p (L l :: RealSrcSpan
l _) = RealSrcSpan -> RealSrcLoc
realSrcSpanEnd RealSrcSpan
l RealSrcLoc -> RealSrcLoc -> Bool
forall a. Ord a => a -> a -> Bool
<= RealSrcSpan -> RealSrcLoc
realSrcSpanStart RealSrcSpan
ref
(RealLocated Comment -> Bool)
-> (RealSrcSpan -> Comment -> R ()) -> R Bool
withPoppedComment RealLocated Comment -> Bool
forall e. GenLocated RealSrcSpan e -> Bool
p ((RealSrcSpan -> Comment -> R ()) -> R Bool)
-> (RealSrcSpan -> Comment -> R ()) -> R Bool
forall a b. (a -> b) -> a -> b
$ \l :: RealSrcSpan
l comment :: Comment
comment -> do
Bool
dirtyLine <-
case Maybe RealSrcSpan
mlastSpn of
Nothing -> R Bool
isLineDirty
Just _ -> Bool -> R Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
Bool -> R () -> R ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Bool
dirtyLine Bool -> Bool -> Bool
|| RealSrcSpan -> Maybe RealSrcSpan -> Bool
needsNewlineBefore RealSrcSpan
l Maybe RealSrcSpan
mlastSpn) R ()
newline
RealSrcSpan -> Comment -> R ()
spitCommentNow RealSrcSpan
l Comment
comment
if RealSrcSpan -> RealSrcSpan -> Bool
theSameLinePre RealSrcSpan
l RealSrcSpan
ref Bool -> Bool -> Bool
&& Bool -> Bool
not (a -> Bool
forall a. Data a => a -> Bool
isModule a
a)
then R ()
space
else R ()
newline
spitFollowingComment ::
Data a =>
RealLocated a ->
Maybe RealSrcSpan ->
R Bool
(L ref :: RealSrcSpan
ref a :: a
a) mlastSpn :: Maybe RealSrcSpan
mlastSpn = do
Maybe RealSrcSpan
mnSpn <- R (Maybe RealSrcSpan)
nextEltSpan
Maybe RealSrcSpan
meSpn <- (RealSrcSpan -> Bool) -> R (Maybe RealSrcSpan)
getEnclosingSpan (RealSrcSpan -> RealSrcSpan -> Bool
forall a. Eq a => a -> a -> Bool
/= RealSrcSpan
ref)
(RealLocated Comment -> Bool)
-> (RealSrcSpan -> Comment -> R ()) -> R Bool
withPoppedComment (RealSrcSpan
-> Maybe RealSrcSpan
-> Maybe RealSrcSpan
-> Maybe RealSrcSpan
-> RealLocated Comment
-> Bool
commentFollowsElt RealSrcSpan
ref Maybe RealSrcSpan
mnSpn Maybe RealSrcSpan
meSpn Maybe RealSrcSpan
mlastSpn) ((RealSrcSpan -> Comment -> R ()) -> R Bool)
-> (RealSrcSpan -> Comment -> R ()) -> R Bool
forall a b. (a -> b) -> a -> b
$ \l :: RealSrcSpan
l comment :: Comment
comment ->
if RealSrcSpan -> RealSrcSpan -> Bool
theSameLinePost RealSrcSpan
l RealSrcSpan
ref Bool -> Bool -> Bool
&& Bool -> Bool
not (a -> Bool
forall a. Data a => a -> Bool
isModule a
a)
then
if Comment -> Bool
isMultilineComment Comment
comment
then R ()
space R () -> R () -> R ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> RealSrcSpan -> Comment -> R ()
spitCommentNow RealSrcSpan
l Comment
comment
else CommentPosition -> RealSrcSpan -> Comment -> R ()
spitCommentPending CommentPosition
OnTheSameLine RealSrcSpan
l Comment
comment
else do
Bool -> R () -> R ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (RealSrcSpan -> Maybe RealSrcSpan -> Bool
needsNewlineBefore RealSrcSpan
l Maybe RealSrcSpan
mlastSpn) (R () -> R ()) -> R () -> R ()
forall a b. (a -> b) -> a -> b
$
CommentPosition -> Text -> R ()
registerPendingCommentLine CommentPosition
OnNextLine ""
CommentPosition -> RealSrcSpan -> Comment -> R ()
spitCommentPending CommentPosition
OnNextLine RealSrcSpan
l Comment
comment
spitRemainingComment ::
Maybe RealSrcSpan ->
R Bool
spitRemainingComment :: Maybe RealSrcSpan -> R Bool
spitRemainingComment mlastSpn :: Maybe RealSrcSpan
mlastSpn =
(RealLocated Comment -> Bool)
-> (RealSrcSpan -> Comment -> R ()) -> R Bool
withPoppedComment (Bool -> RealLocated Comment -> Bool
forall a b. a -> b -> a
const Bool
True) ((RealSrcSpan -> Comment -> R ()) -> R Bool)
-> (RealSrcSpan -> Comment -> R ()) -> R Bool
forall a b. (a -> b) -> a -> b
$ \l :: RealSrcSpan
l comment :: Comment
comment -> do
Bool -> R () -> R ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (RealSrcSpan -> Maybe RealSrcSpan -> Bool
needsNewlineBefore RealSrcSpan
l Maybe RealSrcSpan
mlastSpn) R ()
newline
RealSrcSpan -> Comment -> R ()
spitCommentNow RealSrcSpan
l Comment
comment
R ()
newline
handleCommentSeries ::
(Maybe RealSrcSpan -> R Bool) ->
R Bool
handleCommentSeries :: (Maybe RealSrcSpan -> R Bool) -> R Bool
handleCommentSeries f :: Maybe RealSrcSpan -> R Bool
f = Bool -> R Bool
go Bool
False
where
go :: Bool -> R Bool
go gotSome :: Bool
gotSome = do
Bool
done <- R (Maybe (Maybe HaddockStyle, RealSrcSpan))
getLastCommentSpan R (Maybe (Maybe HaddockStyle, RealSrcSpan))
-> (Maybe (Maybe HaddockStyle, RealSrcSpan) -> R Bool) -> R Bool
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Maybe RealSrcSpan -> R Bool
f (Maybe RealSrcSpan -> R Bool)
-> (Maybe (Maybe HaddockStyle, RealSrcSpan) -> Maybe RealSrcSpan)
-> Maybe (Maybe HaddockStyle, RealSrcSpan)
-> R Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Maybe HaddockStyle, RealSrcSpan) -> RealSrcSpan)
-> Maybe (Maybe HaddockStyle, RealSrcSpan) -> Maybe RealSrcSpan
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Maybe HaddockStyle, RealSrcSpan) -> RealSrcSpan
forall a b. (a, b) -> b
snd
if Bool
done
then Bool -> R Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
gotSome
else Bool -> R Bool
go Bool
True
withPoppedComment ::
(RealLocated Comment -> Bool) ->
(RealSrcSpan -> Comment -> R ()) ->
R Bool
p :: RealLocated Comment -> Bool
p f :: RealSrcSpan -> Comment -> R ()
f = do
Maybe (RealLocated Comment)
r <- (RealLocated Comment -> Bool) -> R (Maybe (RealLocated Comment))
popComment RealLocated Comment -> Bool
p
case Maybe (RealLocated Comment)
r of
Nothing -> Bool -> R Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
Just (L l :: RealSrcSpan
l comment :: Comment
comment) -> Bool
False Bool -> R () -> R Bool
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ RealSrcSpan -> Comment -> R ()
f RealSrcSpan
l Comment
comment
needsNewlineBefore ::
RealSrcSpan ->
Maybe RealSrcSpan ->
Bool
needsNewlineBefore :: RealSrcSpan -> Maybe RealSrcSpan -> Bool
needsNewlineBefore l :: RealSrcSpan
l mlastSpn :: Maybe RealSrcSpan
mlastSpn =
case Maybe RealSrcSpan
mlastSpn of
Nothing -> Bool
False
Just lastSpn :: RealSrcSpan
lastSpn ->
RealSrcSpan -> Int
srcSpanStartLine RealSrcSpan
l Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> RealSrcSpan -> Int
srcSpanEndLine RealSrcSpan
lastSpn Int -> Int -> Int
forall a. Num a => a -> a -> a
+ 1
theSameLinePre ::
RealSrcSpan ->
RealSrcSpan ->
Bool
theSameLinePre :: RealSrcSpan -> RealSrcSpan -> Bool
theSameLinePre l :: RealSrcSpan
l ref :: RealSrcSpan
ref =
RealSrcSpan -> Int
srcSpanEndLine RealSrcSpan
l Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== RealSrcSpan -> Int
srcSpanStartLine RealSrcSpan
ref
theSameLinePost ::
RealSrcSpan ->
RealSrcSpan ->
Bool
theSameLinePost :: RealSrcSpan -> RealSrcSpan -> Bool
theSameLinePost l :: RealSrcSpan
l ref :: RealSrcSpan
ref =
RealSrcSpan -> Int
srcSpanStartLine RealSrcSpan
l Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== RealSrcSpan -> Int
srcSpanEndLine RealSrcSpan
ref
commentFollowsElt ::
RealSrcSpan ->
Maybe RealSrcSpan ->
Maybe RealSrcSpan ->
Maybe RealSrcSpan ->
RealLocated Comment ->
Bool
ref :: RealSrcSpan
ref mnSpn :: Maybe RealSrcSpan
mnSpn meSpn :: Maybe RealSrcSpan
meSpn mlastSpn :: Maybe RealSrcSpan
mlastSpn (L l :: RealSrcSpan
l comment :: Comment
comment) =
Bool
goesAfter
Bool -> Bool -> Bool
&& Bool
logicallyFollows
Bool -> Bool -> Bool
&& Bool
noEltBetween
Bool -> Bool -> Bool
&& (Bool
continuation Bool -> Bool -> Bool
|| Bool
lastInEnclosing Bool -> Bool -> Bool
|| Bool
supersedesParentElt)
where
goesAfter :: Bool
goesAfter =
RealSrcSpan -> RealSrcLoc
realSrcSpanStart RealSrcSpan
l RealSrcLoc -> RealSrcLoc -> Bool
forall a. Ord a => a -> a -> Bool
>= RealSrcSpan -> RealSrcLoc
realSrcSpanEnd RealSrcSpan
ref
logicallyFollows :: Bool
logicallyFollows =
RealSrcSpan -> RealSrcSpan -> Bool
theSameLinePost RealSrcSpan
l RealSrcSpan
ref
Bool -> Bool -> Bool
|| Comment -> Bool
isPrevHaddock Comment
comment
Bool -> Bool -> Bool
|| Bool
continuation
Bool -> Bool -> Bool
|| Bool
lastInEnclosing
noEltBetween :: Bool
noEltBetween =
case Maybe RealSrcSpan
mnSpn of
Nothing -> Bool
True
Just nspn :: RealSrcSpan
nspn ->
RealSrcSpan -> RealSrcLoc
realSrcSpanStart RealSrcSpan
nspn RealSrcLoc -> RealSrcLoc -> Bool
forall a. Ord a => a -> a -> Bool
>= RealSrcSpan -> RealSrcLoc
realSrcSpanEnd RealSrcSpan
l
supersedesParentElt :: Bool
supersedesParentElt =
case Maybe RealSrcSpan
meSpn of
Nothing -> Bool
True
Just espn :: RealSrcSpan
espn ->
let startColumn :: RealSrcSpan -> Int
startColumn = RealSrcLoc -> Int
srcLocCol (RealSrcLoc -> Int)
-> (RealSrcSpan -> RealSrcLoc) -> RealSrcSpan -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RealSrcSpan -> RealSrcLoc
realSrcSpanStart
in RealSrcSpan -> Int
startColumn RealSrcSpan
espn Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> RealSrcSpan -> Int
startColumn RealSrcSpan
ref
Bool -> Bool -> Bool
|| ( Int -> Int
forall a. Num a => a -> a
abs (RealSrcSpan -> Int
startColumn RealSrcSpan
espn Int -> Int -> Int
forall a. Num a => a -> a -> a
- RealSrcSpan -> Int
startColumn RealSrcSpan
l)
Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int -> Int
forall a. Num a => a -> a
abs (RealSrcSpan -> Int
startColumn RealSrcSpan
ref Int -> Int -> Int
forall a. Num a => a -> a -> a
- RealSrcSpan -> Int
startColumn RealSrcSpan
l)
)
continuation :: Bool
continuation =
case Maybe RealSrcSpan
mlastSpn of
Nothing -> Bool
False
Just spn :: RealSrcSpan
spn -> RealSrcSpan -> Int
srcSpanEndLine RealSrcSpan
spn Int -> Int -> Int
forall a. Num a => a -> a -> a
+ 1 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== RealSrcSpan -> Int
srcSpanStartLine RealSrcSpan
l
lastInEnclosing :: Bool
lastInEnclosing =
case Maybe RealSrcSpan
meSpn of
Nothing -> Bool
False
Just espn :: RealSrcSpan
espn ->
let
insideParent :: Bool
insideParent = RealSrcSpan -> RealSrcLoc
realSrcSpanEnd RealSrcSpan
l RealSrcLoc -> RealSrcLoc -> Bool
forall a. Ord a => a -> a -> Bool
<= RealSrcSpan -> RealSrcLoc
realSrcSpanEnd RealSrcSpan
espn
nextOutsideParent :: Bool
nextOutsideParent = case Maybe RealSrcSpan
mnSpn of
Nothing -> Bool
True
Just nspn :: RealSrcSpan
nspn -> RealSrcSpan -> RealSrcLoc
realSrcSpanEnd RealSrcSpan
espn RealSrcLoc -> RealSrcLoc -> Bool
forall a. Ord a => a -> a -> Bool
< RealSrcSpan -> RealSrcLoc
realSrcSpanStart RealSrcSpan
nspn
in Bool
insideParent Bool -> Bool -> Bool
&& Bool
nextOutsideParent
spitCommentNow :: RealSrcSpan -> Comment -> R ()
spn :: RealSrcSpan
spn comment :: Comment
comment = do
R () -> R ()
sitcc
(R () -> R ()) -> (Comment -> R ()) -> Comment -> R ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NonEmpty (R ()) -> R ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_
(NonEmpty (R ()) -> R ())
-> (Comment -> NonEmpty (R ())) -> Comment -> R ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. R () -> NonEmpty (R ()) -> NonEmpty (R ())
forall a. a -> NonEmpty a -> NonEmpty a
NE.intersperse R ()
newline
(NonEmpty (R ()) -> NonEmpty (R ()))
-> (Comment -> NonEmpty (R ())) -> Comment -> NonEmpty (R ())
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> R ()) -> NonEmpty String -> NonEmpty (R ())
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Text -> R ()
txt (Text -> R ()) -> (String -> Text) -> String -> R ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack)
(NonEmpty String -> NonEmpty (R ()))
-> (Comment -> NonEmpty String) -> Comment -> NonEmpty (R ())
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Comment -> NonEmpty String
forall a b. Coercible a b => a -> b
coerce
(Comment -> R ()) -> Comment -> R ()
forall a b. (a -> b) -> a -> b
$ Comment
comment
Maybe HaddockStyle -> RealSrcSpan -> R ()
setLastCommentSpan Maybe HaddockStyle
forall a. Maybe a
Nothing RealSrcSpan
spn
spitCommentPending :: CommentPosition -> RealSrcSpan -> Comment -> R ()
position :: CommentPosition
position spn :: RealSrcSpan
spn comment :: Comment
comment = do
let wrapper :: R () -> R ()
wrapper = case CommentPosition
position of
OnTheSameLine -> R () -> R ()
sitcc
OnNextLine -> R () -> R ()
forall a. a -> a
id
R () -> R ()
wrapper
(R () -> R ()) -> (Comment -> R ()) -> Comment -> R ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [R ()] -> R ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_
([R ()] -> R ()) -> (Comment -> [R ()]) -> Comment -> R ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NonEmpty (R ()) -> [R ()]
forall a. NonEmpty a -> [a]
NE.toList
(NonEmpty (R ()) -> [R ()])
-> (Comment -> NonEmpty (R ())) -> Comment -> [R ()]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> R ()) -> NonEmpty String -> NonEmpty (R ())
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (CommentPosition -> Text -> R ()
registerPendingCommentLine CommentPosition
position (Text -> R ()) -> (String -> Text) -> String -> R ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack)
(NonEmpty String -> NonEmpty (R ()))
-> (Comment -> NonEmpty String) -> Comment -> NonEmpty (R ())
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Comment -> NonEmpty String
forall a b. Coercible a b => a -> b
coerce
(Comment -> R ()) -> Comment -> R ()
forall a b. (a -> b) -> a -> b
$ Comment
comment
Maybe HaddockStyle -> RealSrcSpan -> R ()
setLastCommentSpan Maybe HaddockStyle
forall a. Maybe a
Nothing RealSrcSpan
spn