-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
462 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
[ | ||
{ | ||
"caption": "Lines - Multisets: Sum", | ||
"command": "lines_multisets_choose_view", | ||
"args": { "operation_name": "sum" } | ||
}, | ||
|
||
{ | ||
"caption": "Lines - Multisets: Union", | ||
"command": "lines_multisets_choose_view", | ||
"args": { "operation_name": "union" } | ||
}, | ||
|
||
{ | ||
"caption": "Lines - Multisets: Intersection", | ||
"command": "lines_multisets_choose_view", | ||
"args": { "operation_name": "intersection" } | ||
}, | ||
|
||
{ | ||
"caption": "Lines - Multisets: Difference", | ||
"command": "lines_multisets_choose_view", | ||
"args": { "operation_name": "difference" } | ||
}, | ||
|
||
{ | ||
"caption": "Lines - Multisets: Symmetric Difference", | ||
"command": "lines_multisets_choose_view", | ||
"args": { "operation_name": "symmetric_difference" } | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
[ | ||
{ | ||
"id": "edit", | ||
"children": [ | ||
{ | ||
"id": "lines_multisets", | ||
"caption": "Lines - Multisets", | ||
"children": [ | ||
{ | ||
"caption": "Sum", | ||
"command": "lines_multisets_choose_view", | ||
"args": { "operation_name": "sum" } | ||
}, | ||
|
||
{ | ||
"caption": "Union", | ||
"command": "lines_multisets_choose_view", | ||
"args": { "operation_name": "union" } | ||
}, | ||
|
||
{ | ||
"caption": "Intersection", | ||
"command": "lines_multisets_choose_view", | ||
"args": { "operation_name": "intersection" } | ||
}, | ||
|
||
{ | ||
"caption": "Difference", | ||
"command": "lines_multisets_choose_view", | ||
"args": { "operation_name": "difference" } | ||
}, | ||
|
||
{ | ||
"caption": "Symmetric Difference", | ||
"command": "lines_multisets_choose_view", | ||
"args": { "operation_name": "symmetric_difference" } | ||
} | ||
] | ||
} | ||
] | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,70 @@ | ||
# sublime-lines-multisets | ||
This Sublime Text plugin allows you to compare, merge and manipulate lists of things, whether they are todo items, lists of files or arrays from API responses. | ||
# Sublime Lines - Multisets | ||
This Sublime Text plugin allows you to compare, merge and manipulate lists of things, | ||
whether they are todo items, lists of files or arrays from API responses. | ||
![Comparing a giveaway list with two wishlists](https://github.com/heldev/sublime-lines-multisets/raw/master/demo-giveaway-list.png) | ||
|
||
## How to use | ||
Switch to a file with a list you want to modify and select an operation to perform from the Command Palette | ||
(or using `Edit -> Lines - Multisets` menu). | ||
## Operations | ||
### Add | ||
The result is all items from both lists. Example: | ||
|
||
List 1 | List 2 | ||
-------|------- | ||
one | two | ||
three | three | ||
four | four | ||
four | | ||
|
||
Result: one, two, three, three, four, four | ||
|
||
### [Union](https://en.wikipedia.org/wiki/Set_(mathematics)#Unions) | ||
The result is items from both lists, but only one item for every match. Example: | ||
|
||
List 1 | List 2 | Match | ||
-------|--------|------ | ||
one | two | | ||
three | three | :heavy_check_mark: | ||
four | four | :heavy_check_mark: | ||
four | | | ||
|
||
Result: one, two, three, four, four | ||
|
||
### [Intersection](https://en.wikipedia.org/wiki/Set_(mathematics)#Intersections) | ||
The result is only items that exist in both lists. Example: | ||
|
||
List 1 | List 2 | Match | Result | ||
-------|--------|-------|------- | ||
one | two | | | ||
three | three | :heavy_check_mark: | three | ||
four | four | :heavy_check_mark: | four | ||
four | five | | | ||
five | five | :heavy_check_mark: | five | ||
five | five | :heavy_check_mark: | five | ||
|
||
### [Difference](https://en.wikipedia.org/wiki/Complement_(set_theory)) | ||
The result is items from list 1 that don't have a match in list 2. Example: | ||
|
||
List 1 | List 2 | Match | Result | ||
-------|--------|-------|------- | ||
one | two | | one | ||
three | three | :heavy_check_mark: | | ||
four | four | :heavy_check_mark: | | ||
four | five | | four | ||
five | five | :heavy_check_mark: | | ||
five | five | :heavy_check_mark: | | ||
|
||
### [Symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference) | ||
The result is items that exist in only one of the lists. Example: | ||
|
||
List 1 | List 2 | Match | ||
-------|--------|------ | ||
one | two | | ||
three | three | :heavy_check_mark: | ||
four | four | :heavy_check_mark: | ||
four | five | | ||
five | five | :heavy_check_mark: | ||
five | five | :heavy_check_mark: | ||
|
||
Result: one, two, four, five |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
from functools import partial | ||
|
||
from sublime import Region | ||
from sublime_plugin import TextCommand | ||
|
||
|
||
class LinesMultisetsChooseViewCommand(TextCommand): | ||
|
||
def run(self, _, operation_name): | ||
titles = tuple(map(title, self.other_views)) | ||
|
||
self.view.window().show_quick_panel(titles, partial(self.run_operation, operation_name)) | ||
|
||
def run_operation(self, operation_name, other_view_index): | ||
if other_view_index >= 0: | ||
other_view_id = self.other_views[other_view_index].id() | ||
operation_arguments = {'other_view_id': other_view_id, 'operation_name': operation_name} | ||
self.view.run_command('lines_multisets_operation', operation_arguments) | ||
|
||
@property | ||
def other_views(self): | ||
all_views = self.view.window().views() | ||
|
||
return tuple(filter(self.is_other_view_with_content, all_views)) | ||
|
||
def is_other_view_with_content(self, view): | ||
return view.id() != self.view.id() and content_preview(view) | ||
|
||
|
||
def title(view): | ||
|
||
return view.name() \ | ||
or view.file_name() \ | ||
or '"{}"'.format(content_preview(view)) | ||
|
||
|
||
def content_preview(view): | ||
return view.substr(Region(0, 1024)).strip() |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
from collections import Counter | ||
|
||
from sublime import Region | ||
from sublime_plugin import TextCommand | ||
|
||
|
||
OPERATIONS = { | ||
'sum': lambda a, b: a + b, | ||
'union': lambda a, b: a | b, | ||
'intersection': lambda a, b: a & b, | ||
'difference': lambda a, b: a - b, | ||
'symmetric_difference': lambda a, b: (a | b) - (a & b)} | ||
|
||
|
||
class LinesMultisetsOperationCommand(TextCommand): | ||
|
||
def run(self, edit, other_view_id, operation_name): | ||
operation = OPERATIONS[operation_name] | ||
|
||
current_lines = self.current_line_multiset() | ||
other_lines = self.line_multiset(other_view_id) | ||
|
||
result_text = self.apply_operation(operation, current_lines, other_lines) | ||
|
||
self.display_result(edit, result_text) | ||
|
||
def current_line_multiset(self): | ||
self.view.set_status('lines_multisets', 'Loading multiset 1') | ||
|
||
return lines(self.view) | ||
|
||
def line_multiset(self, view_id): | ||
self.view.set_status('lines_multisets', 'Loading multiset 2') | ||
view = self.find_view(view_id) | ||
|
||
return lines(view) | ||
|
||
def find_view(self, view_id): | ||
all_views = self.view.window().views() | ||
|
||
return next(filter(lambda view: view.id() == view_id, all_views)) | ||
|
||
def apply_operation(self, operation, multiset1, multiset2): | ||
self.view.set_status('lines_multisets', 'Calculating results') | ||
result_lines = sorted(operation(multiset1, multiset2).elements()) | ||
|
||
return '\n'.join(result_lines) | ||
|
||
def display_result(self, edit, result_text): | ||
self.view.set_status('lines_multisets', 'Rendering results') | ||
self.view.replace(edit, max_region(self.view), result_text) | ||
self.view.erase_status('lines_multisets') | ||
|
||
|
||
def lines(view): | ||
return Counter(view.substr(max_region(view)).splitlines()) | ||
|
||
|
||
def max_region(view): | ||
return Region(0, view.size()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
from unittest import TestCase | ||
|
||
import sublime | ||
|
||
|
||
class TestLinesMultisetsChooseViewCommand(TestCase): | ||
|
||
def setUp(self): | ||
self.original_window_class = sublime.Window | ||
sublime.Window = WindowSpy | ||
self.original_view_class = sublime.View | ||
sublime.View = ViewStub | ||
|
||
sublime.run_command('new_window') | ||
self.window = sublime.active_window() | ||
|
||
def tearDown(self): | ||
ViewStub.reset() | ||
WindowSpy.reset() | ||
|
||
for view in self.window.views(): | ||
view.set_scratch(True) | ||
|
||
self.window.run_command('close_window') | ||
|
||
sublime.View = self.original_view_class | ||
sublime.Window = self.original_window_class | ||
|
||
def test_suggest_tab_titles_with_trimmed_content_preview(self): | ||
other_view = self.window.new_file() | ||
other_view.run_command('insert', {'characters': ' lines\n with spaces '}) | ||
|
||
active_view = self.window.new_file() | ||
active_view.run_command('lines_multisets_choose_view', {'operation_name': ''}) | ||
|
||
self.assertIn('"lines\n with spaces"', WindowSpy.quick_panel_items) | ||
|
||
def test_prefer_tab_file_name_over_content_preview(self): | ||
ViewStub.file_name_override = 'unit-test-file.tst' | ||
|
||
other_view = self.window.new_file() | ||
other_view.run_command('insert', {'characters': 'content'}) | ||
|
||
active_view = self.window.new_file() | ||
self.window.focus_view(active_view) | ||
active_view.run_command('lines_multisets_choose_view', {'operation_name': ''}) | ||
|
||
self.assertIn('unit-test-file.tst', WindowSpy.quick_panel_items) | ||
|
||
def test_prefer_tab_name_over_file_name(self): | ||
other_view = self.window.new_file() | ||
other_view.run_command('insert', {'characters': 'content'}) | ||
other_view.set_name('tab name') | ||
|
||
active_view = self.window.new_file() | ||
active_view.run_command('lines_multisets_choose_view', {'operation_name': ''}) | ||
|
||
self.assertIn('tab name', WindowSpy.quick_panel_items) | ||
|
||
def test_not_show_current_and_empty_views(self): | ||
other_view = self.window.new_file() | ||
other_view.set_name('tab name') | ||
|
||
active_view = self.window.new_file() | ||
active_view.run_command('insert', {'characters': 'current_view_with_content'}) | ||
active_view.run_command('lines_multisets_choose_view', {'operation_name': ''}) | ||
|
||
self.assertCountEqual([], WindowSpy.quick_panel_items) | ||
|
||
|
||
class WindowSpy(sublime.Window): | ||
|
||
quick_panel_items = [] | ||
|
||
def show_quick_panel(self, items, on_select, **_): | ||
type(self).quick_panel_items = items | ||
|
||
@classmethod | ||
def reset(cls): | ||
cls.quick_panel_items = [] | ||
|
||
|
||
class ViewStub(sublime.View): | ||
|
||
file_name_override = None | ||
|
||
def file_name(self): | ||
return type(self).file_name_override or super().file_name() | ||
|
||
@classmethod | ||
def reset(cls): | ||
cls.file_name_override = None |
Oops, something went wrong.