As part of PhysLean we have TODO items which are collected using an enviroment extension set up with the following code:
Set up enviroment extension
/-Copyright (c) 2024 Joseph Tooby-Smith. All rights reserved.Released under Apache 2.0 license.Authors: Joseph Tooby-Smith-/importLean.Elab.Command/-!# Basic underlying structure for TODOs.-/namespacePhysLeanopenLean/-- The information from a `TODO ...` command. -/structuretodoInfowhere/-- The content of the note. -/content:String/-- The file name where the note came from. -/fileName:Name/-- The line from where the note came from. -/line:Nat/-- The tag of the TODO item -/tag:String/-- Environment extension to store `todo ...`. -/initializetodoExtension:SimplePersistentEnvExtensiontodoInfo(ArraytodoInfo)←registerSimplePersistentEnvExtension{name:=`todoExtensionaddEntryFn:=funarrtodoInfor=>arr.pushtodoInforaddImportedFn:=funes=>es.foldl(·++·)#[]}/-- Syntax for the `TODO ...` command. -/syntax(name:=todo_comment)"TODO "strstr:command/-- Elaborator for the `TODO ...` command -/@[command_elabtodo_comment]defelabTODO:Elab.Command.CommandElab:=funstx=>matchstxwith|`(TODO$t$s)=>doletstr:String:=s.getStringlettag:String:=t.getStringletpos:=stx.getPos?matchposwith|somepos=>doletenv←getEnvletfileMap←getFileMapletfilePos:=fileMap.toPositionposletline:=filePos.lineletmodName:=env.mainModulelettodoInfo:todoInfo:={content:=str,fileName:=modName,line:=line,tag:=tag}modifyEnvfunenv=>todoExtension.addEntryenvtodoInfo|none=>throwError"Invalid syntax for `TODO` command"|_=>throwError"Invalid syntax for `TODO` command"endPhysLean
An example of a TODO item in PhysLean is:
TODO"6VZJH""For the classical harmonic oscillator find the velocity when it passes through zero."
There are many such items.
An example bit of code for collecting the TODO items is:
Collection code
importPhysLean.Meta.BasicimportPhysLean.Meta.TODO.BasicimportMathlib.Lean.CoreMimportPhysLean.Meta.Informal.PostimportPhysLean.Meta.Informal.SemiFormal/-!# Checks for duplicate TODO tags-/openLeanSystemMetaPhysLeandeftagsFromTODOs:MetaM(ArrayString):=doletenv←getEnvlettodoInfo:=(todoExtension.getStateenv)-- pure (todoInfo.qsort (fun x y => x.fileName.toString < y.fileName.toString)).toListpure(todoInfo.mapfunx=>x.tag)unsafedefinformalTODO(x:ConstantInfo):CoreMString:=dolettag←Informal.getTagxreturntagunsafedeftagsFromInformal:CoreM(ArrayString):=do(←AllInformal).mapM(funx=>Informal.getTagx)unsafedeftagsFromSemiformal:CoreM(ArrayString):=doletenv←getEnvletsemiInformalInfos:=(wantedExtension.getStateenv)pure(semiInformalInfos.mapfunx=>x.tag)unsafedeftagDuplicateTest:MetaMUnit:=dolettags:=((←tagsFromTODOs)++(←tagsFromInformal)++(←tagsFromSemiformal)).toListif!tags.Nodupthenletduplicates:=tags.filter(funtag=>tags.counttag>1)|>.eraseDupsprintln!duplicatespanic!s!"Duplicate tags found: {duplicates}"pure()unsafedefmain(args:ListString):IOUInt32:=doinitSearchPath(←findSysroot)println!"Checking for duplicate tags."let_←CoreM.withImportModules#[`PhysLean](tagDuplicateTest).run'println!"Finish duplicate tag check."pure0
The problem is that this no longer works. In particular let todoInfo := (todoExtension.getState env) returns an array of zero size. I believe this all worked fine on v4.18.0, in particular let todoInfo := (todoExtension.getState env) returned a non-zero array, and the code has not been modified since then.
unsafedefmain(args:ListString):IOUInt32:=doinitSearchPath(←findSysroot)println!"Checking for duplicate tags."let_←CoreM.withImportModules#[`PhysLean](tagDuplicateTest).run'println!"Finish duplicate tag check."pure0
to
openCoreunsafedefmain(args:ListString):IOUInt32:=doinitSearchPath(←findSysroot)println!"Checking for duplicate tags."letenv←importModules(loadExts:=true)#[`PhysLean]{}0letfileName:=""letoptions:Options:={}letctx:Core.Context:={fileName,options,fileMap:=default}letstate:={env}let_←(Lean.Core.CoreM.toIO·ctxstate)do(tagDuplicateTest).run'println!"Finish duplicate tag check."pure0
in my last code block. Not sure this is the cleanest way of doing things within the context of my overall code though, but this is ok.
(For my reference the PR which breaks this is this)