Sublime Text中文文档之 构建系统

Sublime Text提供了构建系统,允许用户运行外部程序。构建系统的常见用途示例包括:编译,转换,linting和执行测试。

构建系统通过JSON指定并保存在扩展名为.sublime-build的文件中。可以通过Tools ▶Build System ▶New Build System ...菜单项或Build: New Build System命令选项板条目创建新的构建系统

构建系统有各种方式可以将自己与文件和项目相关联。使用此信息,Sublime Text可以智能地仅向用户显示可行的构建系统。内置exec目标提供了快速启动和运行的常用选项。对于更复杂的需求,构建系统可以定位用Python编写的自定义Sublime Text命令。

基本例子

以下是构建系统的基本示例。此构建系统将执行当前打开的Python文件。

{
    "cmd": ["python", "$file"],
    "selector": "source.python",
    "file_regex": "^\\s*File \"(...*?)\", line ([0-9]*)"
}

用法选项 部分将讨论如何使用和定制构建系统。

用法

构建系统包括以下功能:

  • 根据文件类型自动选择构建系统
  • 记住上次使用的构建系统
  • 构建系统结果的导航
  • 能够取消构建

运行构建

可以通过以下方法之一运行构建:

键盘 菜单
在Windows / Linux的 苹果电脑 所有
Ctrl + B. + B. F7

输出将显示在Sublime Text窗口底部显示的输出面板中。

选择构建系统

默认情况下,Sublime Text使用自动选择构建系统。当用户调用构建时,将使用当前文件的语法和文件名来选择适当的构建系统。

如果多个构建系统与当前文件类型匹配,系统将提示用户选择他们希望使用的构建系统。一旦选择了构建系统,Sublime Text将记住它,直到用户更改其选择。

要手动选择构建系统,请使用:

菜单

要在可行选项中更改构建系统,请使用以下方法之一:

键盘 菜单 命令调色板
在Windows / Linux的 苹果电脑 Build With:
Ctrl + Shift + B. + + B.

构建系统允许导航构建输出中指定的文件。通常,这用于跳转到错误的位置。可通过以下方式执行导航:

命令 键盘 菜单
下一个结果 F4
上一个结果 Shift + F4

取消构建

可以通过以下方式取消进程内构建:

键盘 菜单 命令调色板
在Windows / Linux的 苹果电脑 Build: Cancel
Ctrl + Break Ctrl + C.

选项

所有构建系统都可以在.sublime-build文件中使用以下顶级键。

选择
应为此构建系统启用的语法的基本作用域名称。
示例: "source.python"
FILE_PATTERNS
应为其启用构建系统的文件名模式列表。
示例: ["*.py"]
密钥文件
文件名列表(如果存在于其中一个打开的文件夹中)将导致构建系统启用。
示例: ["Makefile"]
变种
将从顶级构建系统继承选项的辅助构建系统列表。每个变体都需要指定一个 name键,并可以覆盖或添加顶级构建系统的选项。
例:
[
    {
        "name": "Debug Symbols",
        "cmd": ["my_command", "-D", "$file"]
    }
]
取消
字符串命令名称或字符串选项列表。如果指定了字符串,则指定的命令将用于取消构建。如果是字符串列表,target则会调用主要字符串,并添加这些选项。这只需要在使用自定义时指定target
示例: "cancel_my_build"{"kill": true}
目标
调用构建系统时运行的命令。exec的默认值允许使用exec Target Options中指定的其他选项。如果exec指定的值不是指定的,则exec Target Options中的任何选项都不会执行任何操作。有关完整示例,请参阅 高级示例。
例: "my_build"
视窗
在Windows计算机上执行构建系统时使用的选项对象。
例:
{
    "cmd": ["my_command.exe", "/D", "$file"]
}
OSX
在Mac计算机上执行构建系统时使用的选项对象。
例:
{
    "cmd": ["/Applications/MyProgram.app/Contents/MacOS/my_command", "-d", "$file"]
}
Linux的
在Linux机器上执行构建系统时使用的选项对象。
例:
{
    "cmd": ["/usr/local/bin/my_command", "-d", "$file"]
}

exec 目标选项

默认targetexec使用受到了广大构建系统。它提供以下选项来控制要执行的程序以及如何显示结果。

CMD
指定要运行的可执行文件的字符串列表,以及传递给它的任何参数。不支持Shell构造,例如管道和重定向 - 请参阅 shell_cmd。可以使用变量
例: ["my_command", "-d", "$file"]
shell_cmd
一个字符串,指定要执行的shell命令。与cmd选项不同 ,它允许管道和重定向。将bash在Mac和Linux机器以及cmd.exeWindows上使用。可以使用变量
例: "my_command \"$file\" | other_command"
working_dir
一个字符串,指定要在其中执行cmdshell_cmd的目录 。可以使用变量
例: "$file_path"
file_regex
包含要在构建输出上运行以匹配文件信息的正则表达式的字符串。匹配的文件信息用于启用结果导航。正则表达式应该捕获2,3或4组。

捕获组应该是:
  1. 文件名
  2. 电话号码
  3. 列号
  4. 信息
例: "^\s*(\\S[^:]*)\\((\\d+):(\\d+)\\): ([^\\n]+)"
line_regex
包含要在构建输出上运行以匹配行信息的正则表达式的字符串。匹配的文件信息用于启用结果导航。正则表达式应该捕获1,2或3组。

小组应该抓住:
  1. 电话号码
  2. 列号
  3. 错误信息
只有当某些结果严格包含行号,行号和列号或带有消息的行号和列号时,才需要使用此正则表达式。进行这样的匹配时, 将使用file_regex选项向后搜索以查找适当的文件名。

例: "^\s*line (\\d+) col (\\d+): ([^\\n]+)"
编码
一个字符串,指定构建系统输出的编码。使用 Python编解码器名称。默认为"utf-8"
例: "iso-8859-1"
ENV
包含运行cmdshell_cmd时要使用的环境变量值的对象。
例:
{
    “PYTHONIOENCODING”:“utf-8”
}
安静
一个布尔值,可减少有关构建系统调用的输出量。
例: true
word_wrap
一个布尔值,用于打开构建系统输出面板中的自动换行。
例: true
句法
一个字符串,指定用于突出显示构建系统输出面板的语法文件。
例: "Packages/JavaScript/JSON.sublime-syntax"

自定义选项

当实现命令以充当构建系统目标时,命令的关键字参数可通过.sublime-build文件中的选项获得。但是,某些参数名称不起作用,因为它们与内置的构建系统功能冲突。

以下名称不会作为参数传递给命令。这也适用于其他情况,如在指定的选项cancellinuxosxwindows选项。

  • cancel
  • file_patterns
  • keyfile
  • keyfiles
  • linux
  • osx
  • save_untitled_files
  • selector
  • target
  • variants
  • windows

变量

以下变量将在指定的任何字符串内膨胀"cmd""shell_cmd""working_dir"选项。

如果$需要在其中一个选项中指定文字,则必须使用a进行转义\。由于JSON也使用反斜杠进行转义,$因此需要将其写为\\$

请注意,任何替换都将发生target。如果使用自定义目标,则可以通过使用sublime.expand_variables()结果来 实现其他选项的变量扩展 self.window.extract_variables()

$包
Packages /文件夹 的路径
$平台
包含平台Sublime Text的字符串正在:windowsosx或运行 linux
$文件
活动视图中文件的完整路径,包括文件夹。
$ FILE_PATH
包含活动视图中文件的文件夹的路径。
$ FILE_NAME
活动视图中文件的文件名(无文件夹路径)。
$ file_base_name
活动视图中文件的文件名(不包括扩展名)。
$ FILE_EXTENSION
活动视图中文件的文件名扩展名。
$文件夹
第一个文件夹的完整路径在侧栏中打开。
$项目
当前项目文件的完整路径。
$ project_path
包含当前项目文件的文件夹的路径。
$ PROJECT_NAME
当前项目文件的文件名(sans文件夹路径)。
$ project_base_name
当前项目文件的文件名,不包括扩展名。
$ project_extension
当前项目文件的扩展名。

高级示例

以下示例显示了一个自定义target命令,可以取消构建并导航结果。

一个target用于构建系统应该是一个 sublime.WindowCommand。这将提供实例变量,self.window 以允许与当前项目,窗口和活动视图进行交互。

请注意,以下示例在其实现中有些简单,并且它不会处理许多常见的边缘情况。

以下Python可以保存到名为Package / User / my_example_build.py的文件中 :

import sublime
import sublime_plugin

import subprocess
import threading
import os


class MyExampleBuildCommand(sublime_plugin.WindowCommand):

    encoding = 'utf-8'
    killed = False
    proc = None
    panel = None
    panel_lock = threading.Lock()

    def is_enabled(self, lint=False, integration=False, kill=False):
        # The Cancel build option should only be available
        # when the process is still running
        if kill:
            return self.proc is not None and self.proc.poll() is None
        return True

    def run(self, lint=False, integration=False, kill=False):
        if kill:
            if self.proc:
                self.killed = True
                self.proc.terminate()
            return

        vars = self.window.extract_variables()
        working_dir = vars['file_path']

        # A lock is used to ensure only one thread is
        # touching the output panel at a time
        with self.panel_lock:
            # Creating the panel implicitly clears any previous contents
            self.panel = self.window.create_output_panel('exec')

            # Enable result navigation. The result_file_regex does
            # the primary matching, but result_line_regex is used
            # when build output includes some entries that only
            # contain line/column info beneath a previous line
            # listing the file info. The result_base_dir sets the
            # path to resolve relative file names against.
            settings = self.panel.settings()
            settings.set(
                'result_file_regex',
                r'^File "([^"]+)" line (\d+) col (\d+)'
            )
            settings.set(
                'result_line_regex',
                r'^\s+line (\d+) col (\d+)'
            )
            settings.set('result_base_dir', working_dir)

            self.window.run_command('show_panel', {'panel': 'output.exec'})

        if self.proc is not None:
            self.proc.terminate()
            self.proc = None

        args = ['my_cli']
        if lint:
            args.append('-l')
        elif integration:
            args.append('-i')
        args.append(vars['file_name'])
        self.proc = subprocess.Popen(
            args,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            cwd=working_dir
        )
        self.killed = False

        threading.Thread(
            target=self.read_handle,
            args=(self.proc.stdout,)
        ).start()

    def read_handle(self, handle):
        chunk_size = 2 ** 13
        out = b''
        while True:
            try:
                data = os.read(handle.fileno(), chunk_size)
                # If exactly the requested number of bytes was
                # read, there may be more data, and the current
                # data may contain part of a multibyte char
                out += data
                if len(data) == chunk_size:
                    continue
                if data == b'' and out == b'':
                    raise IOError('EOF')
                # We pass out to a function to ensure the
                # timeout gets the value of out right now,
                # rather than a future (mutated) version
                self.queue_write(out.decode(self.encoding))
                if data == b'':
                    raise IOError('EOF')
                out = b''
            except (UnicodeDecodeError) as e:
                msg = 'Error decoding output using %s - %s'
                self.queue_write(msg  % (self.encoding, str(e)))
                break
            except (IOError):
                if self.killed:
                    msg = 'Cancelled'
                else:
                    msg = 'Finished'
                self.queue_write('\n[%s]' % msg)
                break

    def queue_write(self, text):
        sublime.set_timeout(lambda: self.do_write(text), 1)

    def do_write(self, text):
        with self.panel_lock:
            self.panel.run_command('append', {'characters': text})

MyExampleBuildCommand可以将 自定义配置为构建系统,使用以下JSON保存到名为 Packages / User / My Example Build.sublime-build的文件中

{
    "target": "my_example_build",
    "selector": "source.mylang",
    "cancel": {"kill": true},
    "variants": [
        {
            "name": "Lint",
            "lint": true
        },
        {
            "name": "Integration Tests",
            "integration": true
        }
    ]
}