首页 > 解决方案 > 如何使用 Sublime Text 对具有相同标签的段落进行分组或显示?

问题描述

我在 Sublime Text 中有这样的文本(通常是一个非常大的文本文件):

#tag3
Some notes here about this and that.

#tag1 #tag2
Hello world, here is some text

#tag4
Blah
Blah

#tag2 
Lorem ipsum dolor sit amet, consectetur adipiscing elit, 
sed do eiusmod tempor incididunt ut labore et dolore magna 

#foo
bar

Sublime Text 怎么可能只对与 相关的段落进行分组或显示#tag2是否可以使用“多个光标”或其他技术?

这是所需的输出:#tag2段落首先移动,然后是最后的其余部分:

#tag1 #tag2
Hello world, here is some text

#tag2 
Lorem ipsum dolor sit amet, consectetur adipiscing elit, 
sed do eiusmod tempor incididunt ut labore et dolore magna 

#tag3
Something else

#tag4
Blah
Blah

#foo
bar

我到目前为止尝试过的内容:CTRL+ F#tag2然后浏览不同的匹配项以查找与此标签相关的所有内容。

注意:我不是在寻找命令行方法,而是在 Sulbime Text 中寻找一种方便的方法,以便能够快速浏览带有标签组织的注释/段落的巨大文本文档。

标签: sublimetext3sublimetext2sublimetexttext-editormultiple-cursor

解决方案


没有简单的内置方法来做这样的事情。正如您所提到的,您可以搜索有问题的标签并在结果之间跳过。您还可以尝试制作一个仅匹配您感兴趣的标签(及其内容)的正则表达式,然后执行 aFind All来选择它们并将它们剪切并粘贴到顶部。但是,根据文件的内容,这可能可行,也可能不可行。

一种可能的方法是为您的笔记创建某种自定义语法,以便您可以利用内置的符号列表功能。

%YAML 1.2
---
# See http://www.sublimetext.com/docs/3/syntax.html
scope: text.plain.notes
file_extensions:
  - notes
contexts:
  main:
    - match: '^\s*(#\w+)'
      captures:
        1: entity.name.type
      push:
        - match: '#\w+'
          scope: entity.name.type
        - match: $
          pop: true

如果将这个简单的示例语法(仅限 ST3)应用到您的注释文件,所有标签都将突出显示语法并出现在符号列表中,允许您使用Goto > Goto Symbol.

在这种情况下,标签将按照它们在文件中出现的顺序出现在符号列表中,但是您可以输入一些过滤文本来过滤列表并轻松地在它们之间跳过。您还可以通过Goto > Goto Anything.

此外,将光标放在标签上时,Goto > Goto Defintion...将显示一个快速面板,提供该标签的其他位置(如果有),包括当前文件和其他文件,允许您跳转到适当的位置。

对于诸如您在问题中提到的会改变文件内容的内容,您将需要一个插件。

下面是这样一个插件的一个示例,它假定标签总是单独出现在行上,并且在它们之后直到下一行标签的所有内容都是正文(例如在您的示例文本中布置)。

import sublime
import sublime_plugin

import re

# Regular expressions that match a single tag as well as a line that contains
# one or more tags. Note that the code below presumes that lines with tags
# contain ONLY tags and nothing else.
_tag_regex = r"#\w+"
_tag_line_regex = r"^[ \t]*(?:(?:#\w+)\s*){1,}"

# Command palette input handlers are only supported in Sublime builds 3154 and
# later, so guard against using them in a previous version.
st_ver = int(sublime.version())
HandlerBase = sublime_plugin.ListInputHandler if st_ver >= 3154 else object


class TagInputHandler(HandlerBase):
    """
    Input handler for a command argument named "tag"; tries to provide a list
    of tags that appear in the current file, if any. The selected value becomes
    the value for the argument.
    """
    def __init__(self, view):
        self.view = view

    def placeholder(self):
        return "Tag to move"

    def list_items(self):
        tags = set()
        tag_lines = self.view.find_all(_tag_line_regex)

        for region in tag_lines:
            line = self.view.substr(region)
            tags.update(re.findall(_tag_regex, line))

        if not tags:
            sublime.status_message("File contains no tags")

        return list(sorted(tags))


class TagToTopCommand(sublime_plugin.TextCommand):
    def run(self, edit, tag=None):
        if tag is None:
            return self.prompt_tag()

        # Find all lines that contain tags; they are considered the start of
        # sections.
        lines = self.view.find_all(_tag_line_regex)
        matched = []
        eof = sublime.Region(self.view.size())

        # Keep any tag lines that contain the tag we were given. The found
        # regions are modified so that they start at the start of the tag line
        # and end at the start of the following tag section (or end of file)
        # so that the region entirely encompasses the data for these tags.
        for idx, line in enumerate(lines):
            end = lines[idx + 1] if idx + 1 < len(lines) else eof
            if tag in re.findall(_tag_regex, self.view.substr(line)):
                matched.append(sublime.Region(line.a, end.a))

        # Extract all of the sections that we matched above.
        text = []
        if matched:
            # Go bottom up so we don't disturb our offsets.
            for region in reversed(matched):
                text.append(self.view.substr(region))

                # Special handling if this region ends at the buffer end
                if region.b == eof.b:
                    # Move the start up the region upwards to skip over all
                    # blank lines, so that when we trim this section out there
                    # aren't extra blank lines left at the bottom of the view.
                    prev_line = self.view.line(region.a - 1)
                    while prev_line.a == prev_line.b:
                        prev_line = self.view.line(prev_line.a - 1)
                    region.a = prev_line.b + 1

                    # The region doesn't capture the last line of the buffer,
                    # so ensure that this item is separated from the other
                    # items when it moves.
                    text[-1] += "\n"

                self.view.replace(edit, region, '')

            # Add all of the text back at the beginning, but reverse the order
            # of the entries so they preserve the order they appeared in the
            # file.
            self.view.insert(edit, 0, ''.join(reversed(text)))
        else:
            sublime.status_message("'%s' not found in the current file" % tag)

    def input(self, args):
        # This is invoked by Sublime if the command is executed from the
        # command palette; we make the palette prompt us for the tag if one is
        # not given.
        if args.get("tag", None) is None:
            return TagInputHandler(self.view)

    def prompt_tag(self):
        # This is invoked in situations where the command is executed without a
        # "tag" parameter and it's not executing in the command palette; fall
        # back to prompting via a quick panel
        items = TagInputHandler(self.view).list_items()

        def pick(idx):
            if idx != -1:
                self.view.window().run_command("tag_to_top", {"tag": items[idx]})

        self.view.window().show_quick_panel(
            items,
            lambda idx: pick(idx))

这实现了一个tag_to_top命令,给定一个标签,它将找到所有提到该标签的部分,并将它们按当前文件顺序拉到文件的顶部。

tag参数是可选的;如果没有给出当前文件中所有唯一标签的列表,则将显示在列表中供您选择。您也可以直接将标签传递给它,例如,如果您有一些您经常查看的标签或类似的标签。

{
    "keys": ["super+t"], "command": "tag_to_top",
    //"args": { "tag": "#something" }
},

如果需要,您还可以通过将以下内容添加到TagActions.sublime-commands以您的User包命名的文件中,将其添加到命令面板:

[
    { "caption": "Bring Tag to Top", "command": "tag_to_top" }
]

如上所述,您可以提供一个标签作为参数,可能会使用不同的标签多次添加命令以轻松交换文件。

此外,如果您正在使用支持它的 Sublime 构建,当从命令面板运行时,这将提示您直接在那里输入标签,而不是打开一个快速面板。

请注意,以上内容适用于 Sublime Text 3,但它也应该在 Sublime Text 2 中工作(因为您已对其进行了标记),尽管这仅对我进行了轻度测试。

该示例演示了如何查找文件中代表标签的部分,以便可以将它们从文件中删除并重新添加到顶部。对于非破坏性选项,这可以改为确定与标签匹配的区域并将它们折叠起来,这样只会在缓冲区中立即显示您感兴趣的标签。


推荐阅读