65.9K
CodeProject 正在变化。 阅读更多。
Home

Markdown 计算器

2021年4月1日

CPOL

9分钟阅读

viewsIcon

10401

downloadIcon

114

这只是一个有趣的 Visual Studio Code 扩展,但是……如果有人试图认真使用它,会发生什么呢?

Sample

引言

如果你想创造一些完全无用或荒谬的东西,那就非常认真地去做。只有这样你才有成功的机会。

作者

Content

动机
用法
洞见
设置
控制台
带内联代码的控制台
如何尝试?
实现
代码块的执行
内联代码的执行
计算和控制台 API
下一步?
结论

动机

如今,我们可以用手机拍照,用冰箱上网,和我们的吸尘器说话。话虽如此,我们能用什么作为计算器呢?可能,首先,最自然的东西是某种文本编辑器。

还有别的吗?是的,另一个合适的工具是浏览器。但现在,让我们看看我们能用文本编辑器做什么。

此时,或许是时候说一下,使用电脑的人通常没有有用的东西来做计算。的确,我们如何称呼一个普通的 Windows 或 Linux 应用程序“计算器”为计算器呢?显然,这类应用程序的主要目的是展示电脑出现前的生活有多么困难:用户只有一个微小的低对比度黑白屏幕上的几个数字,不得不按按钮,而无法真正看到正在发生什么。为了让电脑模拟更加不方便,用户被迫用鼠标点击那些按钮。

因此,我向这个非常方便、功能丰富的文本编辑器、开源跨平台 Visual Studio Code 介绍了我的扩展。这个基于 Markdown 的扩展更接近我们从小学就知道的自然计算方式:拿一张纸或一个笔记本,逐行写下计算的步骤。当我们写下我们所做的事情时,我们需要在正常文本行之间写下这些数字和算术运算符:给定什么,我们做什么步骤,以及最终结果是什么。

让我们看看我们该怎么做。

用法

洞见

假设,在这个阳光明媚的春日,你正在写一封情书,并且想在信的末尾送上一百万个吻。当然,你需要一个格式良好且易于维护的文档,所以你使用 Visual Studio CodeMarkdown

作为一名高级工程师,你自然不应该记住常数,也不应该手动做任何事情。你会强烈倾向于写诸如 10 ** 6 kisses 之类的内容。或者,如果你更浪漫,你可能想送 1 << 20 个吻,也就是说,一个兆吻,或者更准确地说,一个Mebikiss

然而,假设你的心上人不太懂电脑语言,甚至不习惯二进制移位。在这种情况下,你想写 10 ** 6,但在输出文档中将其渲染为老式的一百万个吻。

你可以通过使用 VSCode 的 Markdown Calculator 扩展来实现,输入 return 10 ** 6 kisses 来代替 1000000

在你关系更成熟的阶段,你可能需要检查你的开销并提供一些购买证明。你可以在 VSCode 中使用带有“run”关键字的代码块来做到这一点,写在第一行。

~~~ run
const eggs = 3.49, sourCream = 2.49, milk = 4.99
const gel = 19.99, vitamines = 17.99
const taxable = gel + vitamines
const tax = taxable * 6.25 / 100
const total = eggs + sourCream + milk + taxable + tax
console.log(`Total: $${Math.round(total * 100) / 100}`);
~~~

该扩展会将其渲染为计算结果,显示为通过 console.log 创建的控制台内容。在这种情况下,它将是 $51.32。但是,也可以使用 return;它将显示在最后。

你知道,缺少 return 语句等同于 return undefined,而对象 undefined 是该扩展唯一渲染为空字符串的对象,与 null(渲染为“null”)相反。

在所有情况下,所有异常都会被捕获,并且异常消息会使用特殊的 CSS 样式进行渲染。

你只需使用 VSCode 预览就可以做到所有这些。对于更严肃的工作,你可能想将文档转换为 HTML;你可以使用在文章用 Visual Studio Code 编写文章的工具链中描述的“可扩展 Markdown”扩展来完成。它还暴露或提供了许多有用的 Markdown 语法扩展,以及添加更多可用或新扩展的可能性。

设置

已安装的扩展可以在用户或工作区设置中启用或禁用。此外,代码块执行和内联代码执行这两个功能可以独立启用或禁用。此外,“run”关键字是可配置的,以及不同 console 函数输出的不同 CSS 类。

  • Markdown > Calculator > Enable: 启用所有扩展功能,包括语法装饰器
  • Markdown > Calculator > Fenced Code Block > Enable: 启用 Markdown Calculator 对代码块行为的修改
  • Markdown > Calculator > Fenced Code Block > Enable: 启用 Markdown Calculator 对代码块行为的修改
  • Markdown > Calculator > Inline Code > Enable: 启用 Markdown Calculator 对内联代码行为的修改
  • Markdown > Calculator > Execution Indicator: 用于指示代码执行而非常规渲染的标记;默认值:"run"
  • Markdown > Calculator > Keyword Decorator > Color: 用于突出显示执行指示器的背景颜色
  • Markdown > Calculator > Keyword Decorator > Hover Text: 显示执行指示器关键字用途的悬停文本
  • Markdown > Calculator > CSS Class > Exception: 用于异常文本渲染的 CSS 类名
  • Markdown > Calculator > CSS Class > Return: 用于计算返回值的 CSS 类名
  • Markdown > Calculator > CSS Class > Console > Assert: 用于 console.assert 的 CSS 类名
  • Markdown > Calculator > CSS Class > Console > Debug: 用于 console.debug 的 CSS 类名
  • Markdown > Calculator > CSS Class > Console > Dir: 用于 console.dir 的 CSS 类名
  • Markdown > Calculator > CSS Class > Console > Error: 用于 console.error 的 CSS 类名
  • Markdown > Calculator > CSS Class > Console > Info: 用于 console.info 的 CSS 类名
  • Markdown > Calculator > CSS Class > Console > Log: 用于 console.log 的 CSS 类名
  • Markdown > Calculator > CSS Class > Console > Warm: 用于 console.warn 的 CSS 类名

控制台

“控制台”到底是什么?首先,它是一个带有某些 API 的 JavaScript 对象。通常,我们使用某些 JavaScript API(再次强调,强烈推荐 Visual Studio Code),它有一个供开发者使用的控制台。但我们需要一些不同的东西。

使用 Visual Studio Code,编写文档的标准方法是使用 Markdown,而通常的工作流程是左边是 markdown 代码,右边是预览窗格,我们可以立即看到渲染的文档——参见顶部的图片。这就是我们需要获得计算结果的地方。

此外,Markdown 可以被渲染并保存为 HTML 文件;它应该以相同的方式渲染内容。可以使用扩展 Extensible Markdown 来完成。本文就是这样编写的。

使用 console.log 函数的示例上面已经展示

带内联代码的控制台

乍一看,似乎不可能为内联代码片段使用控制台输出,因为内容以关键字 return 开头,后跟单个表达式。这是不正确的;可以使用控制台。这完全取决于那个单一的表达式是什么。例如,这个表达式就可以达到目的

To use the console,
`return {
    a: console.log("return an undefined object property"),
    nothing: undefined}.
    nothing`.

它将渲染:To use the console, return an undefined object property。如果去掉 .nothing,它也会在最后渲染 [object Object]

这样做是因为该扩展会为所有对象渲染返回值,包括 null,但不会渲染 undefined。我特意这样设计,作为一个用于静默返回值的工具。这对于代码块尤其有用,因为缺少 return 的函数实际上返回 undefined

如何尝试?

您无需安装扩展即可进行尝试。您只需要一个可用的 Visual Studio Code。

打开 Visual Studio Code,并将工作目录设置为当前页面上可下载并已解压的源代码根目录。它将是包含主扩展文件“package.json”的目录。

或者,您可以使用提供的工作区文件“MarkdownCalculator.code-workspace”启动 Visual Studio Code。

它将加载扩展项目。此项目附带“launch.json”,用于启动另一个加载了扩展的 Visual Studio Code 进程。要启动它,请按 F5。

尽情享用!

实现

代码块的执行

该应用程序使用了最简单的 markdown-it 技术之一:修改 markdown-it 渲染器在存在关键字时的行为,保存之前的规则函数,并在所有其他情况下调用它。

const previousFenceRenderer = md.renderer.rules.fence;
md.renderer.rules.fence = (tokens, index, ruleOptions, object, renderer) => {
    if (settings.enable && settings.fencedCodeBlock.enable &&
    tokens[index].info.trim() == settings.executionIndicator)
        return `${renderFunction(tokens[index].content)}`;
    else
        return renderDefault(
            tokens, index, ruleOptions, object,
            renderer, previousFenceRenderer);
};

内联代码的执行

内联代码的情况非常相似。关键字不同:它是 return,这也是一个 JavaScript 关键字,因此无法通过设置进行修改。此外,对 renderFunction 的调用中的第二个参数表明需要对内联表达式进行求值。

    const previousInlineCodeRenderer = md.renderer.rules.code_inline;
    md.renderer.rules.code_inline =
        (tokens, index, ruleOptions, object, renderer) => {
            let expressionString = tokens[index].content.trim();
            if (settings.enable && settings.inlineCode.enable
            && expressionString.startsWith(`${inlineKeyword} `))
                return `${renderFunction(expressionString, true)}`;
            else
                return renderDefault(
                    tokens, index, ruleOptions, object,
                    renderer, previousInlineCodeRenderer);
        };

计算和控制台 API

计算基于 Function 对象

这是一个相当微妙的问题,主要是因为 VSCode 环境受到保护,可以防止在代码块或内联代码中放置恶意或粗心的代码。这在另一篇文章JavaScript Playground中有详细描述。

一个有趣的功能是 console 对象的模拟。在执行过程中收集传递给 console 函数的对象,然后使用从设置中获得的 CSS 名称和 style 属性将它们渲染为 HTML。

const consoleApi = {
    lines: [],
    initialize: function() {
        const handleArguments = (elements, cssClass) => {
            this.lines.push({elements: elements, cssClass: cssClass});
        }; //
        const console = {
            assert: (assertion, ...args) => {
                if (!assertion)
                    handleArguments(args, settings.cssClass.console.assert);
            },
            debug: (...args) => {
                handleArguments(args, settings.cssClass.console.debug);
            },
            dir: (...args) => {
                handleArguments(args, settings.cssClass.console.dir);
            },
            error: (...args) => {
                handleArguments(args, settings.cssClass.console.error)
            },
            info: (...args) => {
                handleArguments(args, settings.cssClass.console.info);
            },
            log: (...args) => {
                handleArguments(args, settings.cssClass.console.log);
            },
            warn: (...args) => {
                handleArguments(args, settings.cssClass.console.warn);
            },
        };
        return setReadonly(console);
    },
    render: function() {
        let result = "";
        if (this.lines.length < 1) return result;
        for (let line of this.lines) {
            let renderedLine = "";
            for (let element of line.elements)
                renderedLine += `${element} `;
            renderedLine = renderedLine.trim();
            result +=
                `<p class="${line.cssClass}">${renderedLine}</p>`;
        }; //loop
        return result;
    },
    clear: function() {
        this.lines.splice(0);
    },
};
const console = consoleApi.initialize();

只实现了一部分 console 函数。另请参阅console 对象文档

下一步是什么?

我们提供了一种自然的计算方法,类似于纸上进行的常规计算。接下来是什么?当然,这将是我们每个人在脑海中进行的计算,即心算。

这里是合适算法的草图

  • 应用某种脑读技术
  • 将读取到的思维模式解析为命令,
  • 将命令翻译成 JavaScript 文本,
  • 将文本传递给构造函数 Function
  • 调用该函数,
  • 如果它抛出异常,则捕获它,并以电击的形式发出负面刺激。

结论

思维惯性是件坏事。

我们不应模仿人们过去使用的过时和有限的设备,而应寻找执行简单事情的理性自然方式。今天这是正在编辑的文档中的计算,明天将支持脑读。无论如何,这都将值得一篇单独的文章——只需订阅 Code Project 时事通讯。

© . All rights reserved.