How can I filter a file for lines containing a string in Sublime Text 2?
I want to filter a file I'm editing in Sublime Text 2 for lines contain a certain string, if possible including regular expressions.
Consider the following file:
foo bar
baz
qux
quuux baz
When filtered for ba
, the result should be:
foo bar
baz
quuux baz
How can I do that?
There is also a poor man's line filtering algorithm (or is it lazy?):
- Select string of interest
- Hit Alt+F3 to go into multi-cursor mode on all occurrences
- Hit Control+L to select entire line (on every cursor line)
- Copy-paste selection to another buffer
Sublime Text 2 is an extensible editor with a Python API. You can create new commands (called Plugins) and make them available from the UI.
Adding basic filtering TextCommand plugin
In Sublime Text 2, select Tools » New Plugin and enter the following text:
import sublime, sublime_plugin
def filter(v, e, needle):
# get non-empty selections
regions = [s for s in v.sel() if not s.empty()]
# if there's no non-empty selection, filter the whole document
if len(regions) == 0:
regions = [ sublime.Region(0, v.size()) ]
for region in reversed(regions):
lines = v.split_by_newlines(region)
for line in reversed(lines):
if not needle in v.substr(line):
v.erase(e, v.full_line(line))
class FilterCommand(sublime_plugin.TextCommand):
def run(self, edit):
def done(needle):
e = self.view.begin_edit()
filter(self.view, e, needle)
self.view.end_edit(e)
cb = sublime.get_clipboard()
sublime.active_window().show_input_panel("Filter file for lines containing: ", cb, done, None, None)
Save as filter.py
in ~/Library/Application Support/Sublime Text 2/Packages/User
Integration with UI
To add this plugin to the Edit menu, select Preferences… » Browse Packages and open the User
folder. If a file called Main.sublime-menu
doesn't exist, create it. Add or set the following text to that file:
[
{
"id": "edit",
"children":
[
{"id": "wrap"},
{ "command": "filter" }
]
}
]
This will insert the filter
command call (essentially, filter
is transformed to FilterCommand().run(…)
for the plugin call and Filter for the menu label) just below the wrap
command. See step 11 here for a more detailed explanation why that is.
To assign a keyboard shortcut, open and edit the file Default (OSX).sublime-keymap
on OS X, or the equivalent for other systems, and enter the following:
[
{
"keys": ["ctrl+shift+f"], "command": "filter"
}
]
This will assign the shortcut ⌃⇧F to this command.
To make the command show up in the Commands Palette, you need to create a file named Default.sublime-commands
(or edit an existing one) in the User
folder. The syntax is similar to the menu file you just edited:
[
{ "caption": "Filter Lines in File", "command": "filter" }
]
Multiple entries (enclosed by curly brackets) need to be separated by commas.
Behavior and UI integration screenshots
The command, as implemented, will filter all lines that are part of a selection (the entire lines, not just the selected parts), or, if no selection exists, the entire buffer, for a substring that is entered to the input field (default is the — possibly useless multi-line — clipboard) after the command is triggered. It can easily be extended to e.g. support regular expressions, or only leave lines not matching a certain expression.
Menu item
Commands palette entry
Editor
Adding support for Regular Expressions
To add support for regular expressions, use the following scripts and snippets instead:
filter.py
:
import sublime, sublime_plugin, re
def matches(needle, haystack, is_re):
if is_re:
return re.match(needle, haystack)
else:
return (needle in haystack)
def filter(v, e, needle, is_re = False):
# get non-empty selections
regions = [s for s in v.sel() if not s.empty()]
# if there's no non-empty selection, filter the whole document
if len(regions) == 0:
regions = [ sublime.Region(0, v.size()) ]
for region in reversed(regions):
lines = v.split_by_newlines(region)
for line in reversed(lines):
if not matches(needle, v.substr(line), is_re):
v.erase(e, v.full_line(line))
class FilterCommand(sublime_plugin.TextCommand):
def run(self, edit):
def done(needle):
e = self.view.begin_edit()
filter(self.view, e, needle)
self.view.end_edit(e)
cb = sublime.get_clipboard()
sublime.active_window().show_input_panel("Filter file for lines containing: ", cb, done, None, None)
class FilterUsingRegularExpressionCommand(sublime_plugin.TextCommand):
def run(self, edit):
def done(needle):
e = self.view.begin_edit()
filter(self.view, e, needle, True)
self.view.end_edit(e)
cb = sublime.get_clipboard()
sublime.active_window().show_input_panel("Filter file for lines matching: ", cb, done, None, None)
Main.sublime-menu
:
[
{
"id": "edit",
"children":
[
{"id": "wrap"},
{ "command": "filter" },
{ "command": "filter_using_regular_expression" }
]
}
]
Default (OSX).sublime-keymap
:
[
{
"keys": ["ctrl+shift+f"], "command": "filter"
},
{
"keys": ["ctrl+shift+option+f"], "command": "filter_using_regular_expression"
}
]
A second plugin command, Filter Using Regular Expression will be added below the Filter menu entry.
Default.sublime-commands
:
[
{ "caption": "Filter Lines in File", "command": "filter" },
{ "caption": "Filter Lines in File Using Regular Expression", "command": "filter_using_regular_expression" }
]
There's now a plugin for filtering lines: https://github.com/davidpeckham/FilterLines
It allows filtering and code folding based on strings or regular expressions.
You can use Sublime's built-in capabilities to do this in 3 to 7 key strokes (not including the regex to be matched).
Step 1: Multi-select all matching lines
Option 1: To multi-select all lines containing a substring
- Select the string of interest.
- Hit Alt+F3 to multi-select all occurences.
- Hit Ctrl+L (Expand Selection to Line).
Option 2: To multi-select all lines matching a regexp
- Hit Ctrl+F to open the Find drawer.
- Make sure Regular Expression matching is enabled (Alt+R to toggle).
- Type in the regular expression.
- Hit Alt+Enter to multi-select all matches.
- Hit Ctrl+L (Expand Selection to Line).
Step 2: Do something with those lines
Option 1: To get rid of all lines that are not selected
- Hit Ctrl+C to copy.
- Hit Ctrl+A to select all.
- Hit Ctrl+V to replace selection with the matching lines.
Option 2: To get rid of all lines that are selected
- Hit Ctrl+Shift+K (Delete Line).
Option 3: To extract selected lines to a new file
- Hit Ctrl+C to copy.
- Hit Ctrl+N to open a new file.
- Hit Ctrl+V to paste.