{-# LANGUAGE PatternGuards, ScopedTypeVariables #-}-- Note: If the first line of the file has wrong indentation, some of the-- code might be left outside of the blocksmoduleYi.Syntax.Layout(layoutHandler,State)whereimportYi.SyntaximportYi.Lexer.AleximportYi.PreludeimportPrelude()importData.Maybe(isJust)importData.List(dropWhile)dataBlockOpent=IndentInt-- block opened because of indentation; parameter is the column of it.|Parent-- block opened because of parenthesesderivingShowisParen::BlockOpent->BoolisParen(Paren_)=TrueisParen_=FalsedataIStatet=IState[BlockOpent]-- current block nestingBool-- should we open a compound now ?Int-- last line numberderivingShowtypeStatetlexState=(IStatet,AlexStatelexState)-- | Transform a scanner into a scanner that also adds opening,-- closing and "next" tokens to indicate layout. -- @isSpecial@ predicate indicates a token that starts a compound,-- like "where", "do", ...-- @isIgnore@ predicate indicates a token that is to be ignored for-- layout. (eg. pre-processor directive...)-- @parens@ is a list of couple of matching parenthesis-like tokens-- "()[]{}...".layoutHandler::foralltlexState.(Showt,Eqt)=>(t->Bool)->[(t,t)]->(Tokt->Bool)->(t,t,t)->(Tokt->Bool)->Scanner(AlexStatelexState)(Tokt)->Scanner(StatetlexState)(Tokt)layoutHandlerisSpecialparensisIgnored(openT,closeT,nextT)isGroupOpenlexSource=Scanner{scanLooked=scanLookedlexSource.snd,scanEmpty=error"layoutHandler: scanEmpty",scanInit=(IState[]True(-1),scanInitlexSource),scanRun=\st->letresult=parse(fstst)(scanRunlexSource(sndst))in--trace ("toks = " ++ show (fmap snd result)) $ result}wheredummyAlexState=AlexState{stLexer=error"dummyAlexState: should not be reused for restart!",lookedOffset=maxBound,-- setting this to maxBound ensures nobody ever uses it.stPosn=startPosn}deepestIndent[]=(-1)deepestIndent(Indenti:_)=ideepestIndent(_:levs)=deepestIndentlevsdeepestParen_[]=FalsedeepestParenp(Parent:levs)=p==t||deepestParenplevsdeepestParenp(_:levs)=deepestParenplevsfindParenft=find((==t).f)parensparse::IStatet->[(AlexStatelexState,Tokt)]->[(StatetlexState,Tokt)]parseiSt@(IStatelevelsdoOpenlastLine)toks@((aSt,tok@Tok{tokPosn=Posn_nextOfslinecol}):tokss)-- ignore this token|isIgnoredtok=(st,tok):parse(IStatelevelsdoOpenline)tokss-- start a compound if the rest of the line is empty then skip to it!|doOpen=caseisGroupOpentokof-- check so that the do is not followed by a {False->(st',ttopenT):parse(IState(Indentcol:levels)Falseline)toksTrue->parse(IStatelevelsFalselastLine)toks-- if it's a block opening, we ignore the layout, and just let the "normal" rule-- handle the creation of another level.-- close, or prepare to close, a paren block|Just(openTok,_)<-findParensnd$tokTtok,deepestParenopenToklevels=caselevelsofIndent_:levs->(st',ttcloseT):parse(IStatelevsFalselastLine)toks-- close an indent level inside the paren blockParenopenTok':levs|openTok==openTok'->(st',tok):parse(IStatelevsFalseline)tokss|otherwise->parse(IStatelevsFalseline)toks-- close one level of nesting.[]->error$"Parse: "++showiSt-- pop an indent block|col<deepestIndentlevels=let(_lev:levs)=dropWhileisParenlevelsin(st',ttcloseT):parse(IStatelevsdoOpenlastLine)toks-- drop all paren levels inside the indent-- next item|line>lastLine&&col==deepestIndentlevels=(st',ttnextT):parse(IState(dropWhileisParenlevels)doOpenline)toks-- drop all paren levels inside the indent-- open a paren block|isJust$findParenfst$(tokTtok)=(st',tok):parse(IState(Paren(tokTtok):levels)(isSpecial(tokTtok))line)tokss-- important note: the the token can be both special and an opening. This is the case of the-- haskell 'let' (which is closed by 'in'). In that case the inner block is that of the indentation.-- prepare to open a compound|isSpecial(tokTtok)=(st',tok):parse(IStatelevelsTrueline)tokss|otherwise=(st',tok):parse(IStatelevelsdoOpenline)toksswherest=(iSt,aSt)st'=(iSt,aSt{lookedOffset=maxpeeked(lookedOffsetaSt)})ttt=Tokt0(tokPosntok)peeked=casetokssof[]->maxBound(AlexState{lookedOffset=p},_):_->p-- This function checked the position and kind of the-- next token. We peeked further, and so must-- update the lookedOffset accordingly.-- finish by closing all the indent states.parseiSt@(IState(Indent_:levs)doOpenposn)[]=((iSt,dummyAlexState),TokcloseT0maxPosn):parse(IStatelevsdoOpenposn)[]parse(IState(Paren_:levs)doOpenposn)[]=parse(IStatelevsdoOpenposn)[]parse(IState[]__)[]=[]maxPosn::PosnmaxPosn=Posn(-1)(-1)0-- HACK! here we have collusion between using (-1) here and the parsing of -- OnlineTrees, which relies on the position of the last token to stop-- the parsing.