在LinqPad中创建用于展开表格的超链接和按钮

人工智能 2026-07-09

我有一堆略微复杂的结构,喜欢在LinqPad里把它们Dump出来以便检查。

它们大多是一堆容器对象,每个容器里都包含一个项的列表,以及关于该容器本身的一些元数据。项可以通过某些属性引用同一容器中的其他项,或者引用其他容器中的项(也可以直接引用容器)。

目前这是一个非常手动的过程:查看一个项,识别对另一项的引用,然后在容器列表中逐一导航,找到那个项。我在找能把这个过程简化的方法,把引用转换成“超链接”(或某种按钮),让我能够自动展开(甚至高亮)正确的元素。

在Util库里有没什么办法,或者有其他方式可以实现?还是唯一的选项就是放弃标准的Dump,完全用HTML重建?

编辑:

添加一个示例,并对我想要实现的目标做一些澄清。

让我们看这个程序:

void Main()
{
    Dictionary<string, item2> test = new Dictionary<string, item2>
    {
        {
            "container1",
            new item2
            {
                info21 = "container1",
                items = {
                    new item1 {
                        name = "element1",
                        info11 = "references element3",
                        refContainer = 1,
                        ref1 = 1,
                    },
                    new item1 {
                        name = "element1",
                        info11 = "references element1",
                        refContainer = 0,
                        ref1 = 0,
                    }
                }
            }
        },
        {
            "container2",
            new item2
            {
                info21 = "container2",
                items = {
                    new item1 {
                        name = "element3",
                        info11 = "references element2",
                        refContainer = 0,
                        ref1 = 1,
                    },
                    new item1 {
                        name = "element4",
                        info11 = "references element2",
                        refContainer = 0,
                        ref1 = 1,
                    }
                }
            }
        },
    };

    test.Dump(1);
}

class item1
{
    public string name { get; set; }
    public string info11 { get; set; }
    public int refContainer { get; set; }
    public int ref1 { get; set; }
}

class item2
{
    public string info21 { get; set; }
    public List<item1> items { get; set; } = new List<item1>();
}

对于这个,我得到:

在此输入图片描述

如果我手动展开第一条记录,我得到:

在此输入图片描述

我想要的基本上就是一种能够让我点击 ref1 列中的任意内容,并让相应的元素展开(也可能被高亮显示)的东西。

基本上我点击 ref1 里的1,对应于 element1,这将是结果:

在此输入图片描述

解决方案

目前还不清楚refContainer和 ref1列的作用;如果你想创建自动锚点和超链接,可以使用一个Unicode字符来表示每一个锚点/链接,然后用JavaScript来实现交互性(我是让LinqPad的 AI来编写JavaScript,而不是手写的):

void Main()
{
    Dictionary<string, item2> test = new Dictionary<string, item2>
    {
        {
            "container1",
            new item2
            {
                info21 = "container1",
                items = {
                    new item1 {
                        name = "🆔element1",
                        info11 = "references 🔗element3",
                    },
                    new item1 {
                        name = "🆔element2",
                        info11 = "references 🔗element1",
                    }
                }
            }
        },
        {
            "container2",
            new item2
            {
                info21 = "container2",
                items = {
                    new item1 {
                        name = "🆔element3",
                        info11 = "references 🔗element2",
                    },
                    new item1 {
                        name = "🆔element4",
                        info11 = "references 🔗element2",
                    }
                }
            }
        },
    };

    test.Dump (collapseTo:2);

    SetupCrossReferences();
}

// Scans dumped output for "🆔name" (defines an anchor) and "🔗name" (a link to that anchor).
// Clicking a 🔗 link scrolls to the matching 🆔 element and briefly highlights it.
void SetupCrossReferences ()
{
    Util.HtmlHead.AddStyles ("""
        .xref-link { cursor: pointer; text-decoration: underline; color: #06c; }
        @keyframes xref-flash {
            0%   { background: #ffec80; }
            100% { background: transparent; }
        }
        .xref-flash { animation: xref-flash 1.5s ease-out; }
        """);

    Util.HtmlHead.AddScript ("""
        function SetupCrossReferences() {
            const re = /(🆔|🔗)([\w-]+)/g;
            const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null);
            const nodes = [];
            let n;
            while (n = walker.nextNode()) {
                re.lastIndex = 0;
                if (re.test(n.nodeValue)) nodes.push(n);
            }
            for (const node of nodes) {
                const text = node.nodeValue;
                const frag = document.createDocumentFragment();
                let last = 0, m;
                re.lastIndex = 0;
                while (m = re.exec(text)) {
                    if (m.index > last)
                        frag.appendChild(document.createTextNode(text.slice(last, m.index)));
                    const name = m[2];
                    if (m[1] === '🆔') {
                        const span = document.createElement('span');
                        span.id = 'xref-' + name;
                        span.textContent = name;
                        frag.appendChild(span);
                    } else {
                        const a = document.createElement('a');
                        a.className = 'xref-link';
                        a.textContent = name;
                        a.addEventListener('click', e => {
                            e.preventDefault();
                            const target = document.getElementById('xref-' + name);
                            if (!target) return;
                            RevealAncestors(target);
                            target.scrollIntoView({ behavior: 'smooth', block: 'center' });
                            target.classList.remove('xref-flash');
                            void target.offsetWidth;
                            target.classList.add('xref-flash');
                        });
                        frag.appendChild(a);
                    }
                    last = m.index + m[0].length;
                }
                if (last < text.length)
                    frag.appendChild(document.createTextNode(text.slice(last)));
                node.parentNode.replaceChild(frag, node);
            }
        }

        function RevealAncestors(el) {
            const defaults = { TR:'table-row', TBODY:'table-row-group', THEAD:'table-header-group',
                TD:'table-cell', TH:'table-cell', TABLE:'table' };
            for (let n = el; n && n !== document.body; n = n.parentElement) {
                if (getComputedStyle(n).display === 'none') {
                    n.style.display = '';
                    if (getComputedStyle(n).display === 'none')
                        n.style.display = defaults[n.tagName] || 'block';
                }
            }
        }
        """);

    Util.JS.RunFunction ("SetupCrossReferences");
}

class item1
{
    public string name { get; set; }
    public string info11 { get; set; }
}

class item2
{
    public string info21 { get; set; }
    public List<item1> items { get; set; } = new List<item1>();
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章