26 November 2012
页面工具 |
读者必须了解 Brackets 或 Edge Code,并对 JavaScript 十分熟悉。
中级
Brackets这种新型开源 Web 代码编辑器十分重视可扩展性。事实上,它的某些核心功能(如“快速打开”、“快速编辑” 和 “HTML 代码提示”)均作为扩展实现。
Brackets 具有一个高度模块化的架构,通过尽量保持 Brackets 主代码关注度强制实施 关注点分离,同时仍然能够实现深度定制——这是适应现代 Web 开发人员需求的绝对性要求。
不过,您可能会注意到,虽然 Brackets 自带一些默认扩展,但跳出默认设置似乎仍然存在一定的限制。出于这个原因,新用户可以首先安装额外扩展,如我的博客文章(亟需立即安装的 Brackets/Edge Code 扩展)所述。这样,您将能够访问各种扩展,比如 字符串操作 或 代码段。
这也就是说,作为一名用户,您可能很快想到,“如果只有Brackets 可以 X... 操作难度该有多大?”然而,由于 Brackets 采用 Web 标准构建而成,比如 Brackets:开源 Web 代码编辑器(作者:Adam Lehman)中所述,自定义过程远比您想象的简单得多。这同样是一项非常有益的体验——您可能很快就会被它的魅力所倾倒。
在本文中,我们将介绍 Brackets 扩展的工作原理、如何使用模板或从头开始构建 Brackets 扩展,以及如何最有效地创建开发环境。
注意:虽然 Edge Code 是 Brackets 的一个发行版,并且您也可以用它来构建扩展,但它并非随附于 Debug 菜单,因此在编写扩展方面并不像 Brackets 那么方便。不过,您可以在 Edge Code 中使用通过 Brackets 创建的任意扩展。本文使用 Brackets Sprint 16 编写。
从技术角度而言,扩展只是包含 JavaScript 文件(名为 main.js )的文件夹,由 Brackets 在启动时执行。

当加载并执行完剩余代码后,该应用程序将在以下文件夹中寻找这些扩展:
请注意,Brackets 为禁用的扩展提供了一个专用位置:src/extensions/disabled,因此用户可以轻松暂时禁用这些扩展,而无需删除它们。
要调试 Brackets 内的扩展,请使用以下步骤。



同时,我强烈建议从第二个窗口中执行测试。为正确执行此操作,您必须从此窗口设置开发人员工具,如步骤 1 所示。我的典型设置如下所示:

另一种不错的做法是从单独的 Brackets 源副本中开发代码。这种做法的优点在于,您可以使用最新版本的应用程序代码,从而确保扩展始终保持最新状态。并且无需编辑原始的应用程序源代码,避免了可能产生的一些不良影响。使用 Brackets 库克隆的一种简单方法是从 GitHub 派生或克隆 Brackets 库,如以下步骤所述。

为了更好地理解我的意思,重要的是要明白 Brackets 应用程序实际上由以下两部分组成:
默认情况下,Brackets shell 会执行存储在应用程序内容文件夹(安装应用程序时创建)中的 Web 文件。但是,不建议从此文件夹直接编辑代码。因此,按住 Shift 键启动本机应用程序,该应用程序就能够从另一个源文件夹运行。或者,您也可以使用应用程序源代码工具文件夹下的 setup_for_hacking 脚本。您可以在 GitHub 上,从 Brackets 使用方法 一文中获取此脚本。
虽然您可以理所当然地从头开始编写扩展,但最好使用模板开始编写。有许多选项可供选择。
入手进行扩展开发的一个典型方法是复制粘贴与您要创建的扩展较为接近的现有扩展。这是一种启动项目的有效方法,但请记住,并非所有扩展都是最新扩展,也不必全部展示最新的最佳实践。此外,某些扩展实际上很难读取,如果刚刚接触扩展开发,则难度会更大。
或者,您也可以使用 Brackets 扩展工具包,其中为大家提供了专用模板,您可以直接将它拖放到 src/extensions/dev 文件夹。该工具包还附带了包含大量注释的代码,用以指导您完成首次扩展开发体验,该工具包还提供了多种相关辅助工具。
无论您选择哪种方案,如果感觉比较舒服,我建议将您自己的自定义模板存储到 src/extensions/disabled 文件夹中,以便日后快速访问。只需将它复制到 user extensions 文件夹。
如果您首次看到现有的扩展,可能会感觉有些迷惑。Brackets 扩展就是 JavaScript 模块 ,如 RequireJS 在 Simplified CommonJS WrapperSimplified CommonJS Wrapper 中所述。不过,本教程不会向大家介绍 JavaScript 模块,有关详细信息,请参阅 现代模块化 JavaScript 设计模式(作者:Addy Osmani)。出于演示目的,我们简单假设扩展如下所示:
define(function (require, exports, module) {
'use strict';
// Extension code goes here
console.log("Extension initialized");
});
define 调用内的匿名函数的所有代码都会在应用程序启动时执行。
当然,在绝大部分情况下,您希望构建一个扩展,从而在应用程序的另一部分调用扩展时执行某些代码。例如,当用户选择某个菜单项时,将会促发您的应用程序执行操作。
您可以通过命令 为这种机制编写代码。您通过某个函数注册命令 ID,这样,当该应用程序调用这个 ID 时,便会执行函数。“这是 CommandManager 的任务。”与 Brackets 内的几乎所有内容一样,CommandManager 本身也是一个模块。要从扩展访问其他模块,只需调用专用的brackets.getModule() 方法,传递给该方法与src 文件夹相关的模块的路径。请注意,下面的代码没有指定 JS 文件扩展
var CommandManager = brackets.getModule("command/CommandManager");
要指定CommandManager 执行哪个函数以及何时执行,请指定register(name, command_id, function) 方法。此方法中的参数如下所示:
name 参数是一个可读、易记的名称,同时也被用作用户界面中的菜单标签。command_id 参数是一个字符串,用于以独特的方式标识这一命令。将它的格式设置如下:[author].[my_extension].[menu_name].[my_command_name].function ,是一个用于在用户选择用户界面中的某个菜单项时由该方法调用的参数。当用户选择用户界面中的某个菜单项时,该应用程序会自动使用对应的 ID 调用execute(command_id) 方法,如以下代码示例所示。var COMMAND_ID = "dderaedt.tutorialExt.logHelloWorld.logHelloWorld";
var COMMAND_NAME = "Log Hello World";
function sayHello() {
console.log("Hello World");
}
CommandManager.register(COMMAND_NAME, COMMAND_ID, sayHello);
在此阶段,扩展正常运行但永远不会被调用,因为某种程序想必在调用这个命令 ID。为此,我们通过菜单模块添加菜单项以调用相应的命令 ID,从而执行这个函数。为此,我们将首先访问菜单模块,如下所示:
var Menus = brackets.getModule("command/Menus");
然后,我们将获取要添加项目的菜单(在本示例中是指 File 菜单)句柄。
var fileMenu = Menus.getMenu(Menus.AppMenuBar.FILE_MENU);
最后,我们只需要添加希望触发的命令对应的项,如下所示:
fileMenu.addMenuItem(COMMAND_ID);
最终得到的此 Hello World 扩展的代码如下所示:
define(function (require, exports, module) {
'use strict';
var CommandManager = brackets.getModule("command/CommandManager");
var Menus = brackets.getModule("command/Menus");
var COMMAND_ID = "dderaedt.tutorialExt.LogHelloWorld";
var COMMAND_NAME = "Log Hello World";
function sayHello() {
console.log("Hello World");
}
CommandManager.register(COMMAND_NAME, COMMAND_ID, sayHello);
var fileMenu = Menus.getMenu(Menus.AppMenuBar.FILE_MENU);
fileMenu.addMenuItem(COMMAND_ID);
});
现在,为了提高有效性,我们假设希望扩展在当前文档内生成一些代码。
在 Brackets 中,该工具中打开的文件均显示为 Document 类的实例。为获取当前文档(换句话说,是指用户当前打开和编辑的文件)的引用,我们需要使用 DocumentManager 模块。首先,我们必须获取此模块的引用,此模块位于这个文档文件夹内。
var DocumentManager = brackets.getModule("document/DocumentManager");
现在,从上一节中的代码开始,将sayHello() 函数重命名为addSomeText()。
首先,我们需要在此函数中获取当前文档的引用。
var currentDoc = DocumentManager.getCurrentDocument();
现在,您可以使用 Document API,它可以帮助您处理文本内容。下面是一些可用方法:
getText() 用于返回整个文本内容setText() 用于设置整个文本内容getRange(start, end) 用于返回部分文本内容replaceRange(text, start, end) 用于替换给定位置的文本请注意,文本位置(例如上面的 start 和 end 参数)通过位置对象表示,其中包含两个属性:line(行数)和 ch(字符数)。
在下列情况下,假设我们要在当前的光标位置生成单行注释。由于 Editor 对象负责管理代码编辑,您必须能够访问 EditorManager.getCurrentFullEditor() 方法返回的当前编辑器实例。确保导入对应的模块后,如下所示:
var EditorManager = brackets.getModule("editor/EditorManager");
您可以通过编辑器访问当前文档,如下所示:
var editor = EditorManager.getCurrentFullEditor();
最后,您可以使用编辑器执行与文本选择相关的所有操作,比如:
getSelectedText()setSelection(start, end)selectWordAt(position)getCursorPos()由于您现在能够访问所需的全部内容,因此可以重写 addSomeText() 函数,如下所示:
function addSomeText() {
var currentDoc = DocumentManager.getCurrentDocument();
var editor = EditorManager.getCurrentFullEditor();
var pos = editor.getCursorPos();
currentDoc.replaceRange("//Black magic. Do not modify EVER", pos);
}
当然,这种简单例子只能为您提供这么多指导。您将很快需要学习有关其他模块提供的 API 的更多信息。以下是了解更多相关信息的一些有用资源:
Document、 Editor、 EditorManager、FileUtils,当然还包括所有默认扩展。本产品经 Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License 许可。Adobe 提供超出该许可范围、与本产品包含的代码示例相关的权限。