展示 GitHub Copilot 如何从零快速实现 Markdown 编辑器,是 AI 编码提效的真实案例。
前提条件
第 1 步:创建 Next.js GitHub Codespace 模板(30 秒) 第 2 步:安装特定依赖(30 秒) 第 3 步:删除 index.js 文件中的代码(2 秒) 第 4 步:用注释提示 GitHub Copilot(4 秒) 第 5 步:触发 GitHub Copilot 的建议(18 秒) 第 6 步:保存文件并试用!(30 秒)
第 1 步:打开扩展! 第 2 步:获取生成代码块的解释 第 3 步:为 Markdown 编辑器设置样式 第 4 步:查看 Copilot Chat 的响应 第 5 步:查看结果 第 6 步:继续实验 第 7 步:使用 Copilot Chat 编写单元测试
用 GitHub Copilot 在 2 分钟内构建 Markdown 编辑器真的可能吗?对我来说是可能的(如果我使用 GitHub Copilot)。实际上,我提议用 GitHub Copilot 在 15 分钟的演示中构建 Markdown 编辑器,我的经理似乎有点担心我的想法过于雄心勃勃。但他仍然信任我完成这个任务。令我惊讶的是,GitHub Copilot 生成的 Markdown 编辑器速度比我预期的还要快。在这篇博客文章中,我会教你如何构建一个 Markdown 编辑器!
但在我开始之前,我想调整一下读者的期望。
这是一个教程,帮助你理解如何在日常任务中实际使用 GitHub Copilot。
我们构建的 Markdown 编辑器功能性会强于美观性。换句话说:它看起来可能有点丑。
作为额外内容,我们会使用 Copilot Chat 来改进 Markdown 编辑器的用户界面。不过,这个产品还在技术预览阶段,还没有面向所有人开放。如果你还没有访问权限,可以用 ChatGPT 替代。
我们会使用 React Markdown npm 包。我第一次尝试时,GitHub Copilot 在我没有提示的情况下就导入了这个包,结果证明它是一个有用的包含。
你需要在编辑器中有 GitHub Copilot 的访问权限,才能从本教程中获得最佳效果!
GitHub Copilot 和其他生成式 AI 工具是不确定性的。这意味着这些工具有时会生成不同的输出。你可能会获得略有不同的输出,但可以随意尝试 GitHub Copilot,直到达到你想要的结果。欢迎分享它为你创造的东西!
GitHub Copilot 和其他生成式 AI 工具正在快速发展。本教程中的建议与今天——2023 年 6 月相关,但在不久的将来可能会过时。
创建 GitHub 账户(如果你还没有)
注册 GitHub Copilot
在你选择的编辑器中安装 GitHub Copilot 扩展
现在我们有了一切所需,让我们开始吧!
我使用 GitHub Codespace 模板是为了避免从头开始安装样板代码依赖和设置环境。当我想快速启动概念验证或交付演示时,它就派上用场了。在我们的情况下,我们希望节省安装 Next.js 及其附带依赖的时间。
你可以通过导航到 https://github.com/codespaces/templates 来创建 Next.js GitHub Codespace 模板。然后为 Next.js 选择"使用此模板"按钮。这将触发一个 Codespace 打开,其中包含样板 Next.js 代码和浏览器预览。
使用 GitHub Codespace 模板减少了安装一些样板代码依赖的需要,但我们还有更多的东西要安装。
确保已安装 GitHub Copilot 扩展。请参见此处获取说明。
确保已安装 GitHub Copilot 扩展。请参见此处获取说明。
安装 React Markdown npm 包。为此,在你的 Codespace 终端中运行以下命令:npm install react-markdown
安装 React Markdown npm 包。为此,在你的 Codespace 终端中运行以下命令:npm install react-markdown
安装 styled-components npm 包。为此,在你的 Codespace 终端中运行以下命令:npm install styled-components@latest
安装 styled-components npm 包。为此,在你的 Codespace 终端中运行以下命令:npm install styled-components@latest
每个 GitHub Codespace 模板都包含样板代码,以便开发人员能够快速实验和原型化他们的想法。但是,为了达到我们的目标,我们需要在 index.js 文件中编写不同的代码;我们不需要现有的代码。
保留 index.js 文件,但删除其中的代码。
下面的注释会向 GitHub Copilot 解释我们的总体目标,并为我们的 AI 编程助手提供上下文。从我之前的 DEV 文章《GitHub Copilot 提示工程初学者指南》中了解更多关于这个方法的信息。复制下面的注释并将其粘贴到你的 index.js 文件顶部。
/*
Create a basic markdown editor in Next.js with the following features:
- Use React hooks
- Create state for markdown with the default text "## markdown preview"
- A text area where users can write markdown
- Show a live preview of the markdown text as I type
- Support for basic markdown syntax like headers, bold, italics
- Use React markdown npm package
- The markdown text and resulting HTML should be saved in the component's state and updated in real time
*/
我按了两次 RETURN 键来触发 GitHub Copilot 的建议。通常在提供上下文后,我会按键盘上的 SPACEBAR、ENTER 或 RETURN 来触发 GitHub Copilot 的建议。
💡 当你创建一个新行时,你应该会看到 GitHub Copilot 的建议或幽灵文本。
💡 当状态栏中的 Copilot 图标旋转时,你可以判断出 GitHub Copilot 将会提供建议。我喜欢把它称为"思考"。在下面的 GIF 中,我突出显示了 Copilot 图标,所以你可以看到它在处理时的旋转方式。
💡 当状态栏中的 Copilot 图标旋转时,你可以判断出 GitHub Copilot 将会提供建议。我喜欢把它称为"思考"。在下面的 GIF 中,我突出显示了 Copilot 图标,所以你可以看到它在处理时的旋转方式。
💡 由于 GitHub Copilot 的输出是不确定性的,结果可能会有所不同。
📝 这是 GitHub Copilot 给我的输出:
import React, { useState } from 'react';
import ReactMarkdown from 'react-markdown';
import styled from 'styled-components';
const Index = () => {
const [markdown, setMarkdown] = useState('## markdown preview');
const handleChange = (e) => {
setMarkdown(e.target.value);
};
return (
<div>
<textarea onChange={handleChange} value={markdown} />
<ReactMarkdown>{markdown}</ReactMarkdown>
</div>
);
}
export default Index;
✨ 随意调整输出以符合你的喜好!
GitHub Copilot 生成的代码看起来是正确的,但我们怎么知道它是否有效?让我们尝试在 Markdown 编辑器的输入框中编写,看看它是否能呈现文本的准确实时预览。
我将测试以下 Markdown 元素:
- bullet
- bullet
- this is the markdown for bullets
- ...
I am **bold**
How to make a word **bold**:
- sandwiched it between two asterisks on the left
- and two asterisks on the right
I am *italic*
How to make a word *italic*:
- sandwiched it between one asterisk on the left
- and one asterisk on the right
I am [link](google.com)
How to convert a word into a [link](google.com):
- sandwiched it between an opening and closing bracket
- then place an actual link next to it
- sandwich that link between two parentheses
I am ###### heading
How to convert a word into a ##### heading:
- Preface the word with hashtags
- The more hashtags, the smaller the word gets
- More hashtags indicate the heading is less important
- # Heading 1 - I am super important.
- ## Heading 2 - I am a subtitle
- ### Heading 3 - I am less important
- #### Heading 4 - I am even smaller and less important
💪🏾 我们成功了!我们用 GitHub Copilot 在不到 2 分钟内开发了一个 Markdown 编辑器。
刚才的过程很有趣,但我们还可以做得更多。我们可以编写单元测试、设计 UI 样式,并获取代码说明。这些事情我们都可以独立完成,但与 GitHub Copilot 结对编程会让整个过程有趣得多。
要完成本教程的后半部分,你需要拥有 Copilot Chat 的访问权限。此外,你还需要有效的 GitHub Copilot 订阅才能使用 Copilot Chat。点击此处了解更多信息。
Copilot Chat 在编辑器中提供了类似 ChatGPT 的体验,但它不只是一个聊天窗口。由于 Copilot Chat 深度集成在 IDE 中,因此它能够获取开发者输入的内容,以及开发者收到的错误信息等上下文。
接下来,让我们尝试使用 Copilot Chat 为 Markdown 编辑器设计样式、编写测试并解释代码!
我对这个框架比较熟悉,因此很放心地接受了之前生成的代码。不过,你可能不熟悉 React 或 Next.js,所以生成的代码对你来说可能很陌生。这会带来一个问题,因为你肯定不希望在不了解代码的情况下盲目接受它。理解即将合并到生产环境中的代码至关重要。幸运的是,Copilot Chat 可以对代码进行深入解释。
安装完成后,尝试打开这个扩展。此时应该会显示一个带有欢迎信息的面板。
让 Copilot Chat 为我们解释 handleChange 函数。
首先,选中 handleChange 函数的代码块。
选中后单击鼠标右键。
选择“Explain This”。
完成上述步骤后,Copilot Chat 应该会提供类似下面这样的解释!
这段代码定义了一个名为 handleChange 的函数,它接收一个事件对象作为参数。handleChange 函数通常用作 React 组件中 textarea 元素的事件处理函数。当用户在 textarea 中输入内容时,系统会调用 handleChange 函数,并向其传入一个包含用户输入信息的事件对象。handleChange 函数使用 setMarkdown 函数更新名为 markdown 的状态变量。setMarkdown 函数由 React 库提供的 useState Hook 返回。useState Hook 用于为 React 函数组件添加状态。通过使用 setMarkdown 函数更新 markdown 状态变量,包含这段代码的组件便可以重新渲染,以反映新的状态。总的来说,这段代码定义了一个名为 handleChange 的函数,用于响应用户输入并更新名为 markdown 的状态变量。setMarkdown 函数用于更新状态变量,而 useState Hook 用于为 React 函数组件添加状态。
💡 如果你需要更清楚地理解代码,可以对任何一行代码或代码块重复执行此步骤。
💡 选中的代码行数越多,解释就越不细致。如果想获得整体概述,可以选中整个文件;但如果想获得更细致的解释,则应单独选中各个代码块。
接下来,让我们与 Copilot Chat 沟通,引导它帮助我们设计 Markdown 编辑器的样式。
✍🏿 我向 Copilot Chat 提出的问题是:“你能否提供一份代码,让我的 Markdown 编辑器拥有更现代的 UX 设计?请使用 styled-components。”
我想再次强调,生成式 AI 和 GitHub Copilot 的输出具有非确定性,因此你得到的结果可能有所不同。Copilot Chat 提供了一份用于处理以下任务的代码:
生成的代码如下:
import React, { useState } from 'react';
import ReactMarkdown from 'react-markdown';
import styled from 'styled-components';
const Container = styled.div`
display: flex;
flex-direction: column;
height: 100vh;
width: 100vw;
align-items: center;
justify-content: center;
background-color: #f5f5f5;
`;
const TextArea = styled.textarea`
width: 50%;
height: 50%;
padding: 1rem;
border-radius: 5px;
border: none;
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.1);
font-size: 1rem;
resize: none;
`;
const Preview = styled(ReactMarkdown)`
width: 50%;
height: 50%;
padding: 1rem;
border-radius: 5px;
border: none;
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.1);
font-size: 1rem;
overflow-y: scroll;
background-color: white;
`;
const Index = () => {
const [markdown, setMarkdown] = useState('## markdown preview');
const handleChange = e => {
setMarkdown(e.target.value);
};
return (
<Container>
<TextArea value={markdown} onChange={handleChange} />
<Preview>{markdown}</Preview>
</Container>
);
}
export default Index;
我审查了这段代码,认为它是一种可行的解决方案,因此复制了生成的代码,并将其粘贴到我的 index.js 文件中。
以下是我的最终效果:
我猜这就是 Copilot 眼中的现代 UX 设计。对我来说,它看起来确实好了一点,但仍有改进空间。这正是你作为开发者调整代码、让它符合自己设想的绝佳机会!
我花了二十多分钟试用 Copilot Chat,以提高自己编写提示词的能力,并了解 Copilot 的局限性。我向它提出了各种不同的问题,例如:
以下是它给出的结果:
🧑🏾🎨 发挥你的创造力,打造属于自己的 Markdown 编辑器!
虽然编写单元测试对于任何生产应用的长期维护都至关重要,但这也是一个非常繁琐的过程。我很高兴看到整个行业正在朝着自动化处理样板代码和重复代码的方向发展。
我们可以使用斜杠命令,请求 Copilot Chat 为 Markdown 编辑器生成单元测试。
在 Copilot Chat 中输入 /tests 后,我收到了它建议的单元测试以及一段简短说明。结果如下:
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import Index from './index';
describe('Index', () => {
it('renders the bold, italic, and theme toggle buttons', () => {
const { getByText } = render(<Index />);
expect(getByText('Bold')).toBeInTheDocument();
expect(getByText('Italic')).toBeInTheDocument();
expect(getByText('Dark Mode')).toBeInTheDocument();
});
it('toggles between dark and light mode when the theme toggle button is clicked', () => {
const { getByText, container } = render(<Index />);
const themeToggle = getByText('Dark Mode');
expect(container.firstChild).toHaveStyle('background-color: #fff');
fireEvent.click(themeToggle);
expect(container.firstChild).toHaveStyle('background-color: #000');
fireEvent.click(themeToggle);
expect(container.firstChild).toHaveStyle('background-color: #fff');
});
it('updates the markdown text when the textarea is changed', () => {
const { getByLabelText, getByText } = render(<Index />);
const textarea = getByLabelText('Markdown Input');
fireEvent.change(textarea, { target: { value: '# Hello World!' } });
expect(getByText('# Hello World!')).toBeInTheDocument();
});
it('applies bold formatting to selected text when the bold button is clicked', () => {
const { getByText, getByLabelText } = render(<Index />);
const textarea = getByLabelText('Markdown Input');
fireEvent.change(textarea, { target: { value: 'Hello **World**!' } });
const boldButton = getByText('Bold');
fireEvent.click(boldButton);
expect(textarea.value).toBe('Hello **World**!');
fireEvent.change(textarea, { target: { value: 'Hello **World!**' } });
fireEvent.click(boldButton);
expect(textarea.value).toBe('Hello World!');
});
it('applies italic formatting to selected text when the italic button is clicked', () => {
const { getByText, getByLabelText } = render(<Index />);
const textarea = getByLabelText('Markdown Input');
fireEvent.change(textarea, { target: { value: 'Hello *World*!' } });
const italicButton = getByText('Italic');
fireEvent.click(italicButton);
expect(textarea.value).toBe('Hello *World*!');
fireEvent.change(textarea, { target: { value: 'Hello *World!*' } });
fireEvent.click(italicButton);
expect(textarea.value).toBe('Hello World!');
});
});
这些测试覆盖了该组件的基本功能,包括渲染正确的按钮、在深色模式和浅色模式之间切换、更新 Markdown 文本,以及对选中的文本应用粗体和斜体格式。请注意,这些测试假设 TextArea 和 Preview 组件能够正常工作,因此你可能还需要为这些组件编写额外的测试。
请注意:我没有试过单元测试,所以风险自负。我强烈建议在合并到分支前运行测试并审查代码。
💾 ...就这样完成了!使用 GitHub Copilot 和 Copilot Chat 进行实验对我来说总是很有趣的。如果你想了解更多这样的内容,请在 DEV 上关注我或 GitHub!
👀 另外,我很想听听你在下面评论中的想法。
如需进一步操作,你可以考虑屏蔽此人和/或举报滥用行为