@@ -522,7 +522,6 @@ private def givenContents : ParserFn :=
522522 optionalFn (symbolFn ":" >> termParser.fn)))
523523 (symbolFn "," )
524524
525-
526525/--
527526A metavariable to be discussed in the remainder of the docstring.
528527
@@ -612,6 +611,98 @@ def given (type : Option StrLit := none) (typeIsMeta : flag false) («show» : f
612611 |> Inline.concat
613612 else return .empty
614613
614+ private def givenInstanceContents : ParserFn :=
615+ whitespace >>
616+ sepBy1Fn false
617+ (nodeFn nullKind
618+ (optionalFn (atomicFn (identFn >> symbolFn ":" )) >>
619+ termParser.fn))
620+ (symbolFn "," )
621+
622+ /--
623+ An instance metavariable to be discussed in the remainder of the docstring.
624+
625+ This is similar to {given}, but the resulting variable is marked for instance synthesis
626+ (with `BinderInfo.instImplicit`), and the name is optional.
627+
628+ There are two syntaxes that can be used:
629+ * `` {givenInstance}`T` `` establishes an unnamed instance of type `T`.
630+ * `` {givenInstance}`x : T` `` establishes a named instance `x` of type `T`.
631+
632+ Additionally, the contents of the code literal can be repeated, with comma separators.
633+
634+ If the `show` flag is `false` (default `true`), then the instance is not shown in the docstring.
635+ -/
636+ @[builtin_doc_role]
637+ def givenInstance («show » : flag true ) (xs : TSyntaxArray `inline) :
638+ DocM (Inline ElabInline) := do
639+ let s ← onlyCode xs
640+
641+ let stxs ← parseStrLit givenInstanceContents s
642+ let stxs := stxs.getArgs.mapIdx Prod.mk |>.filterMap fun (n, s) =>
643+ if n % 2 = 0 then some s else none
644+ let mut lctx ← getLCtx
645+ let mut localInstances ← Meta.getLocalInstances
646+ let mut instCounter := 0
647+ for stx in stxs do
648+ let nameColonOpt := stx[0 ][0 ]
649+ let tyStx := stx[1 ]
650+
651+ -- Generate a fresh name if no name is provided
652+ let (userName, hasUserName) ←
653+ if nameColonOpt.isMissing then
654+ instCounter := instCounter + 1
655+ pure (Name.mkSimple s! "inst{ instCounter} " , false )
656+ else
657+ -- nameColonOpt is `identFn >> symbolFn ":"`, so the identifier is the first child
658+ pure (nameColonOpt[0 ].getId, true )
659+
660+ let ty' : Expr ← elabType tyStx
661+ let class ? ← Meta.isClass? ty'
662+ let some className := class ?
663+ | throwError m!"Expected a type class, but got `{.ofExpr ty'}`"
664+
665+ let fv ← mkFreshFVarId
666+ lctx := lctx.mkLocalDecl fv userName ty' BinderInfo.default
667+ localInstances := localInstances.push { fvar := .fvar fv, className }
668+
669+ if hasUserName then
670+ addTermInfo' nameColonOpt[0 ] (.fvar fv)
671+ (lctx? := some lctx) (isBinder := true ) (expectedType? := some ty')
672+
673+ modify fun st => { st with lctx, localInstances }
674+
675+ if «show » then
676+ let text ← getFileMap
677+ let mut outStrs := #[]
678+ let mut failed := false
679+ for stx in stxs do
680+ let nameColonOpt := stx[0 ][0 ]
681+ let thisStr ←
682+ if let some ⟨b', e'⟩ := stx[1 ].getRange? then
683+ -- Has type annotation
684+ if nameColonOpt.isMissing then
685+ -- No name, just show type
686+ pure <| String.Pos.Raw.extract text.source b' e'
687+ else
688+ -- Has name and type, nameColonOpt is `identFn >> symbolFn ":"`
689+ if let some ⟨b, e⟩ := nameColonOpt[0 ].getRange? then
690+ pure <| s! "{ b.extract text.source e} : { b'.extract text.source e'} "
691+ else
692+ failed := true
693+ break
694+ else
695+ failed := true
696+ break
697+ outStrs := outStrs.push thisStr
698+ if failed then
699+ return .code s.getString
700+ else
701+ return outStrs.map Inline.code
702+ |>.toList |>.intersperse (Inline.text ", " ) |>.toArray
703+ |> Inline.concat
704+ else return .empty
705+
615706
616707private def firstToken? (stx : Syntax) : Option Syntax :=
617708 stx.find? fun
0 commit comments