Как сгруппировать или отобразить абзацы с одним и тем же тегом с помощью Sublime Text? - PullRequest
0 голосов
/ 28 августа 2018

У меня есть такой текст в 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, чтобы иметь возможность быстро просматривать гигантский текстовый документ с примечаниями / абзацами, организованными по тегам.

1 Ответ

0 голосов
/ 01 сентября 2018

Нет простого способа сделать что-то подобное. Как вы упомянули, вы можете найти нужный тег и пропустить результаты. Вы также можете попытаться создать регулярное выражение, которое будет соответствовать только интересующим вас тегам (и их содержимому), а затем сделать Find 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 (поскольку вы пометили его), хотя это только слегка проверено с моей стороны.

В примере показано, как найти разделы файла, представляющие тег, чтобы их можно было удалить из файла и добавить обратно вверху. Для неразрушающего варианта это можно адаптировать, чтобы вместо этого определять области, которые DON'T соответствуют тегу, и вместо этого сворачивать их, что оставляло бы только те теги, которые вам интересны, сразу видимыми в буфере .

...