Hope someone can shine a light on this, really annoying.
I'm trying to find all the links in a MarkdownDocument using Markdig in Powershell 7.1.3
It's designed for .NET and it has a set of inherited classes that it uses to represent various types of markdown documents. I'm using a simple helper PS wrapper that helps using a generic method, but I don't think this is a cause.
Let me show you with a simple repro
install-module psmarkdig # provides the Get-MdElement helper
$doc = [Markdig.Parsers.MarkdownParser]::Parse('# blah`n`nHello [this is a link](linkpath.md)')
$links = @(Get-MdElement $doc 'Markdig.Syntax.Inlines.LinkInline') # force even a single match to be an array
OK, so this contains a single link
> $links.Count
1
and the element is the type we expected
> $links[0].GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False LinkInline Markdig.Syntax.Inlines.ContainerInline
I can see that there is an IsImage property with Get-Member
> Get-Member -InputObject $links[0]
TypeName: Markdig.Syntax.Inlines.LinkInline
Name MemberType Definition
---- ---------- ----------
AppendChild Method Markdig.Syntax.Inlines.ContainerInline AppendChild(Markdig.Syntax.Inlines.Inline child)
Clear Method void Clear()
ContainsChild Method bool ContainsChild(Markdig.Syntax.Inlines.Inline childToFind)
ContainsData Method bool ContainsData(System.Object key), bool IMarkdownObject.ContainsData(System.Object key)
ContainsParentOfType Method bool ContainsParentOfType[T]()
DumpTo Method void DumpTo(System.IO.TextWriter writer), void DumpTo(System.IO.TextWriter writer, int level)
EmbraceChildrenBy Method void EmbraceChildrenBy(Markdig.Syntax.Inlines.ContainerInline container)
Equals Method bool Equals(System.Object obj)
FindBestParent Method Markdig.Syntax.Inlines.Inline FindBestParent()
FindDescendants Method System.Collections.Generic.IEnumerable[T] FindDescendants[T]()
FindParentOfType Method System.Collections.Generic.IEnumerable[T] FindParentOfType[T]()
GetData Method System.Object GetData(System.Object key), System.Object IMarkdownObject.GetData(System.Object key)
GetEnumerator Method Markdig.Syntax.Inlines.ContainerInline Enumerator GetEnumerator(), System.Collections.Generic.IEnumerator[Markdig.Syn…
GetHashCode Method int GetHashCode()
GetType Method type GetType()
InsertAfter Method void InsertAfter(Markdig.Syntax.Inlines.Inline next)
InsertBefore Method void InsertBefore(Markdig.Syntax.Inlines.Inline previous)
MoveChildrenAfter Method void MoveChildrenAfter(Markdig.Syntax.Inlines.Inline parent)
Remove Method void Remove()
RemoveData Method bool RemoveData(System.Object key), bool IMarkdownObject.RemoveData(System.Object key)
ReplaceBy Method Markdig.Syntax.Inlines.Inline ReplaceBy(Markdig.Syntax.Inlines.Inline inline, bool copyChildren)
SetData Method void SetData(System.Object key, System.Object value), void IMarkdownObject.SetData(System.Object key, System.Object v…
ToPositionText Method string ToPositionText()
ToString Method string ToString()
Column Property int Column {get;set;}
FirstChild Property Markdig.Syntax.Inlines.Inline FirstChild {get;}
GetDynamicUrl Property Markdig.Syntax.Inlines.LinkInline GetUrlDelegate GetDynamicUrl {get;set;}
IsAutoLink Property bool IsAutoLink {get;set;}
IsClosed Property bool IsClosed {get;set;}
IsImage Property bool IsImage {get;set;}
IsShortcut Property bool IsShortcut {get;set;}
Label Property string Label {get;set;}
LabelSpan Property System.Nullable[Markdig.Syntax.SourceSpan] LabelSpan {get;set;}
LastChild Property Markdig.Syntax.Inlines.Inline LastChild {get;}
Line Property int Line {get;set;}
NextSibling Property Markdig.Syntax.Inlines.Inline NextSibling {get;}
Parent Property Markdig.Syntax.Inlines.ContainerInline Parent {get;}
PreviousSibling Property Markdig.Syntax.Inlines.Inline PreviousSibling {get;}
Reference Property Markdig.Syntax.LinkReferenceDefinition Reference {get;set;}
Span Property Markdig.Syntax.SourceSpan Span {get;set;}
Title Property string Title {get;set;}
TitleSpan Property System.Nullable[Markdig.Syntax.SourceSpan] TitleSpan {get;set;}
Url Property string Url {get;set;}
UrlSpan Property System.Nullable[Markdig.Syntax.SourceSpan] UrlSpan {get;set;}
However Format-List gives me a very limited view
> Format-List -InputObject $links[0]
IsFirstCharacterEscaped : False
Parent : {this is a link}
PreviousSibling :
NextSibling :
IsClosed : False
Column : 0
Line : 0
Content : this is a link
Span : 0-0
Ok, so perhaps there is some FormattingTypeViews going on, but even throwing -force -property * and -TheKitchenSink doesn't seem to make any difference!
> Format-List -InputObject $links[0] -Property * -Force
IsFirstCharacterEscaped : False
Parent : {this is a link}
PreviousSibling :
NextSibling :
IsClosed : False
Column : 0
Line : 0
Content : this is a link
Span : 0-0
I can see the value of IsImage if I call it
> $links[0].IsImage
False
How can I explore these objects in the normal way that I'm used to doing in Powershell using Format-List? Is it a Powershell bug that I should raise somehow?
CodePudding user response:
Markdig.Syntax.Inlines.LinkInline
itself implements IEnumerable
, which cause Format-List
to enumerate it and report the properties of the enumerated elements, even when passed via -InputObject
.
Specifically, it enumerates the instance's one child element, which is of type Markdig.Syntax.Inlines.LiteralInline
, and its properties are being displayed.
To prevent this enumeration, i.e. to see the properties of the instance itself, you must wrap it in an aux. single-element array:
Format-List -InputObject (, $links[0])
Output:
Url : linkpath.md
GetDynamicUrl :
Title :
Label :
IsImage : False
IsShortcut : False
IsAutoLink : False
Reference :
ParentBlock :
FirstChild : this is a link
LastChild : this is a link
Parent : {blah, Markdig.Syntax.Inlines.CodeInline, nHello , this is a link}
PreviousSibling : nHello
NextSibling :
IsClosed : True
Column : 0
Line : 0
UrlSpan : 0-0
TitleSpan : 0--1
LabelSpan : 0-0
Span : 0-0
A simpler way to get just the property names is via the intrinsic .psobject
property:
$links[0].psobject.Properties.Name
Note:
The problem boils down to
Markdig.Syntax.Inlines.LinkInline
not being thought of as a collection-like type, yet its implementation of theIEnumerable
interface makes PowerShell treat it like a collection.For .NET types to be PowerShell-friendly, they need to expose enumerations via a property that implements
IEnumerable
, say.Children
in this case, rather than implementingIEnumerable
themselves.