Skip to content

Commit

Permalink
Plugin initial version
Browse files Browse the repository at this point in the history
  • Loading branch information
heldev committed Dec 24, 2017
1 parent adf54da commit 4e4133b
Show file tree
Hide file tree
Showing 9 changed files with 462 additions and 2 deletions.
31 changes: 31 additions & 0 deletions Default.sublime-commands
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" }
}
]
42 changes: 42 additions & 0 deletions Main.sublime-menu
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" }
}
]
}
]
}
]
72 changes: 70 additions & 2 deletions README.md
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
38 changes: 38 additions & 0 deletions choose_view.py
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()
Binary file added demo-giveaway-list.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 60 additions & 0 deletions execute_operation.py
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())
92 changes: 92 additions & 0 deletions tests/test_choose_view.py
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
Loading

0 comments on commit 4e4133b

Please sign in to comment.