feat: add CompletionInfo

This commit is contained in:
Leonardo de Moura 2021-04-02 20:44:57 -07:00
parent f631bd8df9
commit ff1d27370c
5 changed files with 63 additions and 48 deletions

View file

@ -37,9 +37,18 @@ structure CommandInfo where
stx : Syntax
deriving Inhabited
structure DotCompletionInfo extends TermInfo where
field? : Option Syntax := none
expectedType? : Option Expr
inductive CompletionInfo where
| dot (termInfo : TermInfo) (field? : Option Syntax) (expectedType? : Option Expr)
| id (stx : Syntax) (expectedType? : Option Expr)
| option (stx : Syntax)
| endSection (stx : Syntax)
-- TODO `import`
def CompletionInfo.stx : CompletionInfo → Syntax
| dot i .. => i.stx
| id stx .. => stx
| option stx => stx
| endSection stx => stx
structure FieldInfo where
name : Name
@ -71,7 +80,7 @@ inductive Info where
| ofCommandInfo (i : CommandInfo)
| ofMacroExpansionInfo (i : MacroExpansionInfo)
| ofFieldInfo (i : FieldInfo)
| ofDotCompletionInfo (i : DotCompletionInfo)
| ofCompletionInfo (i : CompletionInfo)
deriving Inhabited
inductive InfoTree where
@ -128,12 +137,12 @@ def ContextInfo.toPPContext (info : ContextInfo) (lctx : LocalContext) : PPConte
def ContextInfo.ppSyntax (info : ContextInfo) (lctx : LocalContext) (stx : Syntax) : IO Format := do
ppTerm (info.toPPContext lctx) stx
private def formatStxRange (cinfo : ContextInfo) (stx : Syntax) : Format := do
private def formatStxRange (ctx : ContextInfo) (stx : Syntax) : Format := do
let pos := stx.getPos?.getD 0
let endPos := stx.getTailPos?.getD pos
return f!"{fmtPos pos stx.getHeadInfo}-{fmtPos endPos stx.getTailInfo}"
where fmtPos pos info :=
let pos := format <| cinfo.fileMap.toPosition pos
let pos := format <| ctx.fileMap.toPosition pos
match info with
| SourceInfo.original .. => pos
| _ => f!"{pos}†"
@ -141,55 +150,60 @@ where fmtPos pos info :=
def TermInfo.runMetaM (info : TermInfo) (ctx : ContextInfo) (x : MetaM α) : IO α :=
ctx.runMetaM info.lctx x
def TermInfo.format (cinfo : ContextInfo) (info : TermInfo) : IO Format := do
info.runMetaM cinfo do
return f!"{← Meta.ppExpr info.expr} : {← Meta.ppExpr (← Meta.inferType info.expr)} @ {formatStxRange cinfo info.stx}"
def TermInfo.format (ctx : ContextInfo) (info : TermInfo) : IO Format := do
info.runMetaM ctx do
return f!"{← Meta.ppExpr info.expr} : {← Meta.ppExpr (← Meta.inferType info.expr)} @ {formatStxRange ctx info.stx}"
def CommandInfo.format (cinfo : ContextInfo) (info : CommandInfo) : IO Format := do
return f!"command @ {formatStxRange cinfo info.stx}"
def CompletionInfo.format (ctx : ContextInfo) (info : CompletionInfo) : IO Format :=
match info with
| CompletionInfo.dot i .. => return f!"[.] {← i.format ctx}"
| _ => return f!"[.] {info.stx}"
def FieldInfo.format (cinfo : ContextInfo) (info : FieldInfo) : IO Format := do
cinfo.runMetaM info.lctx do
return f!"{info.name} : {← Meta.ppExpr (← Meta.inferType info.val)} := {← Meta.ppExpr info.val} @ {formatStxRange cinfo info.stx}"
def CommandInfo.format (ctx : ContextInfo) (info : CommandInfo) : IO Format := do
return f!"command @ {formatStxRange ctx info.stx}"
def ContextInfo.ppGoals (cinfo : ContextInfo) (goals : List MVarId) : IO Format :=
def FieldInfo.format (ctx : ContextInfo) (info : FieldInfo) : IO Format := do
ctx.runMetaM info.lctx do
return f!"{info.name} : {← Meta.ppExpr (← Meta.inferType info.val)} := {← Meta.ppExpr info.val} @ {formatStxRange ctx info.stx}"
def ContextInfo.ppGoals (ctx : ContextInfo) (goals : List MVarId) : IO Format :=
if goals.isEmpty then
return "no goals"
else
cinfo.runMetaM {} (return Std.Format.prefixJoin "\n" (← goals.mapM Meta.ppGoal))
ctx.runMetaM {} (return Std.Format.prefixJoin "\n" (← goals.mapM Meta.ppGoal))
def TacticInfo.format (cinfo : ContextInfo) (info : TacticInfo) : IO Format := do
let cinfoB := { cinfo with mctx := info.mctxBefore }
let cinfoA := { cinfo with mctx := info.mctxAfter }
let goalsBefore ← cinfoB.ppGoals info.goalsBefore
let goalsAfter ← cinfoA.ppGoals info.goalsAfter
return f!"Tactic @ {formatStxRange cinfo info.stx}\nbefore {goalsBefore}\nafter {goalsAfter}"
def TacticInfo.format (ctx : ContextInfo) (info : TacticInfo) : IO Format := do
let ctxB := { ctx with mctx := info.mctxBefore }
let ctxA := { ctx with mctx := info.mctxAfter }
let goalsBefore ← ctxB.ppGoals info.goalsBefore
let goalsAfter ← ctxA.ppGoals info.goalsAfter
return f!"Tactic @ {formatStxRange ctx info.stx}\nbefore {goalsBefore}\nafter {goalsAfter}"
def MacroExpansionInfo.format (cinfo : ContextInfo) (info : MacroExpansionInfo) : IO Format := do
let before ← cinfo.ppSyntax info.lctx info.before
let after ← cinfo.ppSyntax info.lctx info.after
def MacroExpansionInfo.format (ctx : ContextInfo) (info : MacroExpansionInfo) : IO Format := do
let before ← ctx.ppSyntax info.lctx info.before
let after ← ctx.ppSyntax info.lctx info.after
return f!"Macro expansion\n{before}\n===>\n{after}"
def Info.format (cinfo : ContextInfo) : Info → IO Format
| ofTacticInfo i => i.format cinfo
| ofTermInfo i => i.format cinfo
| ofCommandInfo i => i.format cinfo
| ofMacroExpansionInfo i => i.format cinfo
| ofFieldInfo i => i.format cinfo
| ofDotCompletionInfo i => return f!"[.] {← i.format cinfo}"
def Info.format (ctx : ContextInfo) : Info → IO Format
| ofTacticInfo i => i.format ctx
| ofTermInfo i => i.format ctx
| ofCommandInfo i => i.format ctx
| ofMacroExpansionInfo i => i.format ctx
| ofFieldInfo i => i.format ctx
| ofCompletionInfo i => i.format ctx
partial def InfoTree.format (tree : InfoTree) (cinfo? : Option ContextInfo := none) : IO Format := do
partial def InfoTree.format (tree : InfoTree) (ctx? : Option ContextInfo := none) : IO Format := do
match tree with
| ofJson j => return toString j
| hole id => return toString id
| context i t => format t i
| node i cs => match cinfo? with
| node i cs => match ctx? with
| none => return "<context-not-available>"
| some cinfo =>
| some ctx =>
if cs.size == 0 then
i.format cinfo
i.format ctx
else
return f!"{← i.format cinfo}{Std.Format.nestD <| Std.Format.prefixJoin "\n" (← cs.toList.mapM fun c => format c cinfo?)}"
return f!"{← i.format ctx}{Std.Format.nestD <| Std.Format.prefixJoin "\n" (← cs.toList.mapM fun c => format c ctx?)}"
section
variable [Monad m] [MonadInfoTree m]

View file

@ -1076,7 +1076,7 @@ def mkTermInfo (stx : Syntax) (e : Expr) : TermElabM (Sum Info MVarId) := do
/-- Store in the `InfoTree` that `e` is a "dot"-completion target. -/
def addDotCompletionInfo (stx : Syntax) (e : Expr) (expectedType? : Option Expr) (field? : Option Syntax := none) : TermElabM Unit := do
pushInfoLeaf <| Info.ofDotCompletionInfo { expr := e, stx := stx, lctx := (← getLCtx), field? := field?, expectedType? := expectedType? }
pushInfoLeaf <| Info.ofCompletionInfo <| CompletionInfo.dot { expr := e, stx := stx, lctx := (← getLCtx) } (field? := field?) (expectedType? := expectedType?)
/--
Main function for elaborating terms.

View file

@ -26,7 +26,8 @@ private def isBlackListedForCompletion (declName : Name) : MetaM Bool := do
open Elab in open Meta in
partial def find? (fileMap : FileMap) (hoverPos : String.Pos) (infoTree : InfoTree) : IO (Option CompletionList) := do
let ⟨hoverLine, _⟩ := fileMap.toPosition hoverPos
if let some (ctx, Info.ofDotCompletionInfo info) := infoTree.foldInfo (init := none) (choose fileMap hoverLine) then
match infoTree.foldInfo (init := none) (choose fileMap hoverLine) with
| some (ctx, Info.ofCompletionInfo (CompletionInfo.dot info ..)) =>
info.runMetaM ctx do
dbg_trace ">> {info.stx}, {info.expr}"
let type ← instantiateMVars (← inferType info.expr)
@ -42,11 +43,10 @@ partial def find? (fileMap : FileMap) (hoverPos : String.Pos) (infoTree : InfoTr
return some { label := cinfo.name.getString!, detail? := some (toString detail), documentation? := none }
return some { items := items, isIncomplete := true }
| _ => return none
else
return none
| _ => return none
where
choose (fileMap : FileMap) (hoverLine : Nat) (ctx : ContextInfo) (info : Info) (best? : Option (ContextInfo × Info)) : Option (ContextInfo × Info) :=
if !info.isDotCompletion then best?
if !info.isCompletion then best?
else if let some d := info.occursBefore? hoverPos then
let pos := info.tailPos?.get!
let ⟨line, _⟩ := fileMap.toPosition pos

View file

@ -371,10 +371,11 @@ section RequestHandling
let doc ← st.docRef.get
let text := doc.meta.text
let pos := text.lspPosToUtf8Pos p.position
dbg_trace ">> handleCompletion invoked {pos}"
withWaitFindSnap doc (fun s => s.endPos > pos)
(notFoundX := pure { items := #[], isIncomplete := true }) fun snap => do
for infoTree in snap.toCmdState.infoState.trees do
for (ctx, info) in infoTree.getDotCompletionInfos do
for (ctx, info) in infoTree.getCompletionInfos do
dbg_trace "{← info.format ctx}"
if let some r ← Completion.find? doc.meta.text pos infoTree then
return r

View file

@ -44,14 +44,14 @@ def Info.isTerm : Info → Bool
| ofTermInfo _ => true
| _ => false
def Info.isDotCompletion : Info → Bool
| ofDotCompletionInfo .. => true
def Info.isCompletion : Info → Bool
| ofCompletionInfo .. => true
| _ => false
def InfoTree.getDotCompletionInfos (infoTree : InfoTree) : Array (ContextInfo × DotCompletionInfo) :=
def InfoTree.getCompletionInfos (infoTree : InfoTree) : Array (ContextInfo × CompletionInfo) :=
infoTree.foldInfo (init := #[]) fun ctx info result =>
match info with
| Info.ofDotCompletionInfo info => result.push (ctx, info)
| Info.ofCompletionInfo info => result.push (ctx, info)
| _ => result
def Info.stx : Info → Syntax
@ -60,7 +60,7 @@ def Info.stx : Info → Syntax
| ofCommandInfo i => i.stx
| ofMacroExpansionInfo i => i.before
| ofFieldInfo i => i.stx
| ofDotCompletionInfo i => i.stx
| ofCompletionInfo i => i.stx
def Info.pos? (i : Info) : Option String.Pos :=
i.stx.getPos? (originalOnly := true)