From 2b3dcf4a847eacdd42d16e21cee0b7a5c17899e9 Mon Sep 17 00:00:00 2001 From: Starli0n Date: Fri, 17 Mar 2017 14:40:03 +0100 Subject: [PATCH 01/25] Platform specific configuration and Environment variable expansion --- file_diffs.py | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/file_diffs.py b/file_diffs.py index db4ee71..633a51f 100644 --- a/file_diffs.py +++ b/file_diffs.py @@ -51,8 +51,16 @@ def on_done(index): class FileDiffCommand(sublime_plugin.TextCommand): - def settings(self): - return sublime.load_settings('FileDiffs.sublime-settings') + def get_setting(self, key, default=None): + settings = sublime.load_settings('FileDiffs.sublime-settings') + os_specific_settings = {} + if sublime.platform() == 'windows': + os_specific_settings = sublime.load_settings('FileDiffs (Windows).sublime-settings') + elif sublime.platform() == 'osx': + os_specific_settings = sublime.load_settings('FileDiffs (OSX).sublime-settings') + else: + os_specific_settings = sublime.load_settings('FileDiffs (Linux).sublime-settings') + return os_specific_settings.get(key, settings.get(key, default)) def diff_content(self, view): content = '' @@ -72,7 +80,7 @@ def prep_content(self, ab, file_name, default_name): file_name = default_name content = [line.replace("\r\n", "\n").replace("\r", "\n") for line in content] - trim_trailing_white_space_before_diff = self.settings().get('trim_trailing_white_space_before_diff', False) + trim_trailing_white_space_before_diff = self.get_setting('trim_trailing_white_space_before_diff', False) if trim_trailing_white_space_before_diff: content = [line.rstrip() for line in content] @@ -96,8 +104,8 @@ def run_diff(self, a, b, from_file, to_file, **options): else: self.view.show_popup('No Difference') else: - external_command = external_diff_tool or self.settings().get('cmd') - open_in_sublime = self.settings().get('open_in_sublime', not external_command) + external_command = external_diff_tool or self.get_setting('cmd') + open_in_sublime = self.get_setting('open_in_sublime', not external_command) if external_command: self.diff_with_external(external_command, a, b, from_file, to_file, **options) @@ -135,7 +143,7 @@ def diff_with_external(self, external_command, a, b, from_file=None, to_file=Non with codecs.open(to_file, encoding='utf-8', mode='w+') as tmp_file: tmp_file.write(b) - trim_trailing_white_space_before_diff = self.settings().get('trim_trailing_white_space_before_diff', False) + trim_trailing_white_space_before_diff = self.get_setting('trim_trailing_white_space_before_diff', False) if trim_trailing_white_space_before_diff: def trim_trailing_white_space(file_name): trim_lines = [] @@ -162,12 +170,13 @@ def trim_trailing_white_space(file_name): if os.path.exists(from_file): external_command = [c.replace('$file1', from_file) for c in external_command] external_command = [c.replace('$file2', to_file) for c in external_command] + external_command = [os.path.expandvars(c) for c in external_command] if sublime.platform() == "windows": Popen(external_command) else: subprocess.Popen(external_command) - apply_tempfile_changes_after_diff_tool = self.settings().get('apply_tempfile_changes_after_diff_tool', False) + apply_tempfile_changes_after_diff_tool = self.get_setting('apply_tempfile_changes_after_diff_tool', False) post_diff_tool = options.get('post_diff_tool') if apply_tempfile_changes_after_diff_tool and post_diff_tool is not None and (not from_file_exists or not to_file_exists): if from_file_exists: @@ -344,11 +353,11 @@ def on_done(index): def find_files(self, folders, ret=[]): # Cannot access these settings!! WHY!? - # folder_exclude_patterns = self.view.settings().get('folder_exclude_patterns') - # file_exclude_patterns = self.view.settings().get('file_exclude_patterns') + # folder_exclude_patterns = self.view.get_setting('folder_exclude_patterns') + # file_exclude_patterns = self.view.get_setting('file_exclude_patterns') folder_exclude_patterns = [".svn", ".git", ".hg", "CVS"] file_exclude_patterns = ["*.pyc", "*.pyo", "*.exe", "*.dll", "*.obj", "*.o", "*.a", "*.lib", "*.so", "*.dylib", "*.ncb", "*.sdf", "*.suo", "*.pdb", "*.idb", ".DS_Store", "*.class", "*.psd", "*.db"] - max_files = self.settings().get('limit', 1000) + max_files = self.get_setting('limit', 1000) for folder in folders: if not os.path.isdir(folder): @@ -406,7 +415,7 @@ def on_post_diff_tool(from_file, to_file): if len(files) == 1: on_done(0) else: - if self.settings().get('expand_full_file_name_in_tab', False): + if self.get_setting('expand_full_file_name_in_tab', False): menu_items = [[os.path.basename(f),f] for f in files] else: menu_items = [os.path.basename(f) for f in files] From 8d4393b4aa867ef0562e916dc38849c867a295e6 Mon Sep 17 00:00:00 2001 From: Starli0n Date: Mon, 20 Mar 2017 19:50:21 +0100 Subject: [PATCH 02/25] Update README with new features --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 69fac41..5f92c77 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,11 @@ Shows diffs between the current file, or selection(s) in the current file, and c ## Preview -![Preview] (https://github.com/ildarkhasanshin/SublimeFileDiffs/raw/master/preview_1.png) +![Preview](https://github.com/ildarkhasanshin/SublimeFileDiffs/raw/master/preview_1.png) -![Preview] (https://github.com/ildarkhasanshin/SublimeFileDiffs/raw/master/preview_2.png) +![Preview](https://github.com/ildarkhasanshin/SublimeFileDiffs/raw/master/preview_2.png) -![Preview] (https://github.com/ildarkhasanshin/SublimeFileDiffs/raw/master/preview_3.png) +![Preview](https://github.com/ildarkhasanshin/SublimeFileDiffs/raw/master/preview_3.png) -------------- @@ -57,6 +57,11 @@ Add External Diff Tool *(optional)* This command *may* need to be a full path (e.g. `/usr/local/bin/ksdiff`), if the command isn't in your `PATH`. +It supports: + +- A generic setting `FileDiffs.sublime-settings` which could be overloaded for each parameter in a platform specific configuration `FileDiffs ($platform).sublime-settings` in the `Settings - User` +- Environment variable expansions for `cmd` parameter in the settings + Commands -------- From 30c98092ea7ab85775a4d0763110e923e6e8b803 Mon Sep 17 00:00:00 2001 From: David Millar Date: Tue, 31 Oct 2017 11:19:58 -0500 Subject: [PATCH 03/25] Updated menu item text to be consistent with other menus and to be more clear on its behavior. --- Tab Context.sublime-menu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tab Context.sublime-menu b/Tab Context.sublime-menu index 2a7fc9e..7950291 100644 --- a/Tab Context.sublime-menu +++ b/Tab Context.sublime-menu @@ -1,6 +1,6 @@ [ { "caption": "-" }, { "caption": "FileDiffs Menu", "command": "file_diff_menu" }, - { "caption": "Diff with Tab…", "command": "file_diff_tab" }, + { "caption": "Diff tab with Open Tab…", "command": "file_diff_tab" }, { "caption": "-" } ] From 4d372baade203c7d85e462959e5864ac336bb528 Mon Sep 17 00:00:00 2001 From: David Millar Date: Tue, 31 Oct 2017 11:24:35 -0500 Subject: [PATCH 04/25] Cleaned up README doc. --- README.md | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5f92c77..f159a47 100644 --- a/README.md +++ b/README.md @@ -23,28 +23,37 @@ Check the [wiki][] for more tips Installation ------------ +### Package Control (Easiest) + 1. Using Package Control, install "FileDiffs" -Or: +### Sublime Text 3 1. Open the Sublime Text Packages folder - OS X: ~/Library/Application Support/Sublime Text 3/Packages/ - Windows: %APPDATA%/Sublime Text 3/Packages/ - Linux: ~/.Sublime Text 3/Packages/ or ~/.config/sublime-text-3/Packages -2. clone this repo +2. Clone this repo + + # Over SSH + git clone git@github.com:colinta/SublimeFileDiffs + + # Over HTTPS + git clone https://github.com/colinta/SublimeFileDiffs.git + 3. Install keymaps for the commands (see Example.sublime-keymap for my preferred keys) ### Sublime Text 2 1. Open the Sublime Text 2 Packages folder -2. clone this repo, but use the `st2` branch - - git clone -b st2 git@github.com:colinta/SublimeFileDiffs +2. Clone this repo, but use the `st2` branch - or + # Over SSH + git clone -b st2 git@github.com:colinta/SublimeFileDiffs - git clone -b st2 https://github.com/colinta/SublimeFileDiffs.git + # Over HTTPS + git clone -b st2 https://github.com/colinta/SublimeFileDiffs.git Add External Diff Tool *(optional)* -------- @@ -90,4 +99,4 @@ Thanks to: - **Sebastian Pape** for adding support for using an external diff tool - **Starli0n** for merging the ST2 and ST3 branches into one branch, - and for adding the "Diff file with previous" feature -- **dnsmkl** for helping with diffing temporary files +- **dnsmkl** for helping with diffing temporary files \ No newline at end of file From 33a07853c53150bdf6601dbf5762b514b11ae790 Mon Sep 17 00:00:00 2001 From: David Millar Date: Tue, 31 Oct 2017 11:26:07 -0500 Subject: [PATCH 05/25] More readme cleanup. --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f159a47..84c3398 100644 --- a/README.md +++ b/README.md @@ -30,17 +30,17 @@ Installation ### Sublime Text 3 1. Open the Sublime Text Packages folder - - OS X: ~/Library/Application Support/Sublime Text 3/Packages/ - - Windows: %APPDATA%/Sublime Text 3/Packages/ - - Linux: ~/.Sublime Text 3/Packages/ or ~/.config/sublime-text-3/Packages + - OS X: `~/Library/Application Support/Sublime Text 3/Packages/` + - Windows: `%APPDATA%/Sublime Text 3/Packages/` + - Linux: `~/.Sublime Text 3/Packages/` or `~/.config/sublime-text-3/Packages` 2. Clone this repo - # Over SSH + ```# Over SSH git clone git@github.com:colinta/SublimeFileDiffs # Over HTTPS - git clone https://github.com/colinta/SublimeFileDiffs.git + git clone https://github.com/colinta/SublimeFileDiffs.git``` 3. Install keymaps for the commands (see Example.sublime-keymap for my preferred keys) @@ -49,11 +49,11 @@ Installation 1. Open the Sublime Text 2 Packages folder 2. Clone this repo, but use the `st2` branch - # Over SSH + ```# Over SSH git clone -b st2 git@github.com:colinta/SublimeFileDiffs # Over HTTPS - git clone -b st2 https://github.com/colinta/SublimeFileDiffs.git + git clone -b st2 https://github.com/colinta/SublimeFileDiffs.git``` Add External Diff Tool *(optional)* -------- From 70bdb4daa93432de592af96812bc2e64ee4605cf Mon Sep 17 00:00:00 2001 From: David Millar Date: Tue, 31 Oct 2017 11:27:08 -0500 Subject: [PATCH 06/25] One more revision to readme. --- README.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 84c3398..9929f28 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Installation ### Package Control (Easiest) -1. Using Package Control, install "FileDiffs" +1. Using Package Control, install `FileDiffs` ### Sublime Text 3 @@ -36,11 +36,13 @@ Installation 2. Clone this repo - ```# Over SSH + ``` + # Over SSH git clone git@github.com:colinta/SublimeFileDiffs # Over HTTPS - git clone https://github.com/colinta/SublimeFileDiffs.git``` + git clone https://github.com/colinta/SublimeFileDiffs.git + ``` 3. Install keymaps for the commands (see Example.sublime-keymap for my preferred keys) @@ -49,11 +51,13 @@ Installation 1. Open the Sublime Text 2 Packages folder 2. Clone this repo, but use the `st2` branch - ```# Over SSH + ``` + # Over SSH git clone -b st2 git@github.com:colinta/SublimeFileDiffs # Over HTTPS - git clone -b st2 https://github.com/colinta/SublimeFileDiffs.git``` + git clone -b st2 https://github.com/colinta/SublimeFileDiffs.git + ``` Add External Diff Tool *(optional)* -------- From 7cd946fc40c1d69046c081aa7a678f34d6816051 Mon Sep 17 00:00:00 2001 From: "Colin T.A. Gray" Date: Mon, 18 Jun 2018 10:12:18 -0600 Subject: [PATCH 07/25] rm st2 support from readme --- README.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/README.md b/README.md index 5f92c77..ae2719c 100644 --- a/README.md +++ b/README.md @@ -35,17 +35,6 @@ Or: 2. clone this repo 3. Install keymaps for the commands (see Example.sublime-keymap for my preferred keys) -### Sublime Text 2 - -1. Open the Sublime Text 2 Packages folder -2. clone this repo, but use the `st2` branch - - git clone -b st2 git@github.com:colinta/SublimeFileDiffs - - or - - git clone -b st2 https://github.com/colinta/SublimeFileDiffs.git - Add External Diff Tool *(optional)* -------- From 28ff059c2036c77b7186387b18a9e96702dde136 Mon Sep 17 00:00:00 2001 From: Tim Robertson Date: Sat, 27 Oct 2018 03:05:21 -0400 Subject: [PATCH 08/25] Add new setting for switching clipboard old and new --- file_diffs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/file_diffs.py b/file_diffs.py index 633a51f..2d1845a 100644 --- a/file_diffs.py +++ b/file_diffs.py @@ -252,7 +252,8 @@ def on_post_diff_tool(from_file, to_file): self.update_view(self.view, edit, from_file) sublime.set_clipboard(self.get_content_from_file(to_file)) - kwargs.update({'post_diff_tool': on_post_diff_tool}) + reverse = kwargs.get('reverse') or self.get_setting('reverse_clipboard', False) + kwargs.update({'post_diff_tool': on_post_diff_tool, 'reverse': reverse}) self.run_diff(self.diff_content(self.view), clipboard, from_file=from_file, to_file='(clipboard)', From d6097ce9ff40067fbe0c409f34eaca5cf353ccf3 Mon Sep 17 00:00:00 2001 From: "Colin T.A. Gray" Date: Mon, 29 Oct 2018 10:37:43 -0400 Subject: [PATCH 09/25] only support ST3 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 5c3e54c..06c997b 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "releases": [ { "details": "https://github.com/colinta/SublimeFileDiffs/tags", - "sublime_text": "*" + "sublime_text": ">=3000" } ] -} \ No newline at end of file +} From 58f1d97ab4d4ef8a4995ec3f82e62bc813b2571c Mon Sep 17 00:00:00 2001 From: David Millar Date: Fri, 2 Nov 2018 15:36:24 -0500 Subject: [PATCH 10/25] Updated with keymaps info on package control instructions. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 984998a..fdc9afc 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,8 @@ Installation 1. Using Package Control, install `FileDiffs` +2. Install keymaps for the commands (see Example.sublime-keymap for my preferred keys) + ### Sublime Text 3 1. Open the Sublime Text Packages folder From 306121b0726cc802650e57663c35ad70aa6dc4b2 Mon Sep 17 00:00:00 2001 From: Ali Ayas Date: Mon, 25 May 2020 04:38:34 +0300 Subject: [PATCH 11/25] When a file is dirty in a tab, use dirty buffer Internal diffing already has this behaviour. This commit applies it to external tools. --- README.md | 2 +- file_diffs.py | 25 +++++++++++-------------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index fdc9afc..b151909 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ The rest of the commands do not need to be bound (accessible from the menu): `file_diff_tab`: Shows the diff of the current file or selection(s) and an open file (aka a file that has a tab). -`file_diff_previous`: Shows the diff of the current file or selection(s) and the previous activated file. +`file_diff_previous`: Shows the diff of the current file or selection(s) and the previous activated file. If a file is not saved yet, dirty buffer is used instead of reading from disk. Contributors ------------ diff --git a/file_diffs.py b/file_diffs.py index 2d1845a..bd20cd0 100644 --- a/file_diffs.py +++ b/file_diffs.py @@ -117,17 +117,10 @@ def run_diff(self, a, b, from_file, to_file, **options): def diff_with_external(self, external_command, a, b, from_file=None, to_file=None, **options): try: - try: - from_file_exists = os.path.exists(from_file) - except ValueError: - from_file_exists = False + from_file_on_disk = self.file_will_be_read_from_disk(from_file) + to_file_on_disk = self.file_will_be_read_from_disk(to_file) - try: - to_file_exists = os.path.exists(to_file) - except ValueError: - to_file_exists = False - - if not from_file_exists: + if not from_file_on_disk: tmp_file = tempfile.NamedTemporaryFile(delete=False) from_file = tmp_file.name tmp_file.close() @@ -135,7 +128,7 @@ def diff_with_external(self, external_command, a, b, from_file=None, to_file=Non with codecs.open(from_file, encoding='utf-8', mode='w+') as tmp_file: tmp_file.write(a) - if not to_file_exists: + if not to_file_on_disk: tmp_file = tempfile.NamedTemporaryFile(delete=False) to_file = tmp_file.name tmp_file.close() @@ -178,10 +171,10 @@ def trim_trailing_white_space(file_name): apply_tempfile_changes_after_diff_tool = self.get_setting('apply_tempfile_changes_after_diff_tool', False) post_diff_tool = options.get('post_diff_tool') - if apply_tempfile_changes_after_diff_tool and post_diff_tool is not None and (not from_file_exists or not to_file_exists): - if from_file_exists: + if apply_tempfile_changes_after_diff_tool and post_diff_tool is not None and (not from_file_on_disk or not to_file_on_disk): + if from_file_on_disk: from_file = None - if to_file_exists: + if to_file_on_disk: to_file = None # Use a dialog to block st and wait for the closing of the diff tool if sublime.ok_cancel_dialog("Apply changes from tempfile after external diff tool execution?"): @@ -234,6 +227,10 @@ def update_view(self, view, edit, tmp_file): return view.replace(edit, region, self.get_content_from_file(tmp_file)) + def file_will_be_read_from_disk(self, file): + view = self.view.window().find_open_file(file) + return bool(os.path.exists(file) and not (view and view.is_dirty())) + class FileDiffDummy1Command(sublime_plugin.TextCommand): def run(self, edit, content): From 21c269019ad0a2f9d87f6e06a123efb5c20f16a1 Mon Sep 17 00:00:00 2001 From: Ali Ayas Date: Mon, 25 May 2020 04:51:21 +0300 Subject: [PATCH 12/25] A fix for the "diff against saved" case --- file_diffs.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/file_diffs.py b/file_diffs.py index bd20cd0..4d3fc67 100644 --- a/file_diffs.py +++ b/file_diffs.py @@ -120,6 +120,14 @@ def diff_with_external(self, external_command, a, b, from_file=None, to_file=Non from_file_on_disk = self.file_will_be_read_from_disk(from_file) to_file_on_disk = self.file_will_be_read_from_disk(to_file) + # If a dirty file is diffed against the copy on disk, we statically set + # `from_file_on_disk` to True; so that the diff becomes "file to temp" + # instead of "temp to temp". + if to_file == from_file + " (Unsaved)": + view = self.view.window().find_open_file(from_file) + if os.path.exists(from_file) and view and view.is_dirty(): + from_file_on_disk = True + if not from_file_on_disk: tmp_file = tempfile.NamedTemporaryFile(delete=False) from_file = tmp_file.name From 0e05ce1617c3805c53091c38a6e9b96695c6b914 Mon Sep 17 00:00:00 2001 From: Ali Ayas Date: Tue, 26 May 2020 00:46:31 +0300 Subject: [PATCH 13/25] Use "Packages" folder for temp files to improve portability Leaving files in the system temp folder is not good for portable installation of Sublime Text. --- file_diffs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/file_diffs.py b/file_diffs.py index 4d3fc67..0ca39fe 100644 --- a/file_diffs.py +++ b/file_diffs.py @@ -129,7 +129,7 @@ def diff_with_external(self, external_command, a, b, from_file=None, to_file=Non from_file_on_disk = True if not from_file_on_disk: - tmp_file = tempfile.NamedTemporaryFile(delete=False) + tmp_file = tempfile.NamedTemporaryFile(dir = sublime.packages_path(), prefix = "file-diffs-", suffix = ".temp", delete=False) from_file = tmp_file.name tmp_file.close() @@ -137,7 +137,7 @@ def diff_with_external(self, external_command, a, b, from_file=None, to_file=Non tmp_file.write(a) if not to_file_on_disk: - tmp_file = tempfile.NamedTemporaryFile(delete=False) + tmp_file = tempfile.NamedTemporaryFile(dir = sublime.packages_path(), prefix = "file-diffs-", suffix = ".temp", delete=False) to_file = tmp_file.name tmp_file.close() @@ -158,7 +158,7 @@ def trim_trailing_white_space(file_name): if trim_line != line: modified = True if modified: - tmp_file = tempfile.NamedTemporaryFile(delete=False) + tmp_file = tempfile.NamedTemporaryFile(dir = sublime.packages_path(), prefix = "file-diffs-", suffix = ".temp", delete=False) file_name = tmp_file.name tmp_file.close() with codecs.open(file_name, encoding='utf-8', mode='w+') as f: From fbbcc430d1e8cb1c644b4d88beee2c050abe0744 Mon Sep 17 00:00:00 2001 From: Ali Ayas Date: Tue, 26 May 2020 21:46:14 +0300 Subject: [PATCH 14/25] Remove temporary files after 15 seconds P.S. This feature works fine with "apply_tempfile_changes_after_diff_tool" setting. In that case, timer is started after the user closes yes/no dialog. --- README.md | 2 ++ file_diffs.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/README.md b/README.md index b151909..4f2abfb 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,8 @@ The rest of the commands do not need to be bound (accessible from the menu): `file_diff_previous`: Shows the diff of the current file or selection(s) and the previous activated file. If a file is not saved yet, dirty buffer is used instead of reading from disk. +If FileDiffs has to use temporary files, they are created in your `Data/Packages` folder (rather than system temp folder) due to privacy concerns for portable Sublime Text installations. Temporary files are automatically removed after 15 seconds. + Contributors ------------ diff --git a/file_diffs.py b/file_diffs.py index 0ca39fe..96cc591 100644 --- a/file_diffs.py +++ b/file_diffs.py @@ -128,10 +128,13 @@ def diff_with_external(self, external_command, a, b, from_file=None, to_file=Non if os.path.exists(from_file) and view and view.is_dirty(): from_file_on_disk = True + files_to_remove = [] + if not from_file_on_disk: tmp_file = tempfile.NamedTemporaryFile(dir = sublime.packages_path(), prefix = "file-diffs-", suffix = ".temp", delete=False) from_file = tmp_file.name tmp_file.close() + files_to_remove.append(tmp_file.name) with codecs.open(from_file, encoding='utf-8', mode='w+') as tmp_file: tmp_file.write(a) @@ -140,6 +143,7 @@ def diff_with_external(self, external_command, a, b, from_file=None, to_file=Non tmp_file = tempfile.NamedTemporaryFile(dir = sublime.packages_path(), prefix = "file-diffs-", suffix = ".temp", delete=False) to_file = tmp_file.name tmp_file.close() + files_to_remove.append(tmp_file.name) with codecs.open(to_file, encoding='utf-8', mode='w+') as tmp_file: tmp_file.write(b) @@ -161,6 +165,7 @@ def trim_trailing_white_space(file_name): tmp_file = tempfile.NamedTemporaryFile(dir = sublime.packages_path(), prefix = "file-diffs-", suffix = ".temp", delete=False) file_name = tmp_file.name tmp_file.close() + files_to_remove.append(tmp_file.name) with codecs.open(file_name, encoding='utf-8', mode='w+') as f: f.writelines('\n'.join(trim_lines)) return file_name @@ -191,6 +196,15 @@ def trim_trailing_white_space(file_name): # some basic logging here, since we are cluttering the /tmp folder sublime.status_message(str(e)) + finally: + def remove_files(): + for file in files_to_remove: + os.remove(file) + + # Remove temp files after 15 seconds. We don't remove immediately, + # because external diff tools may take some time to read them from disk. + sublime.set_timeout_async(remove_files, 15000) + def diff_in_sublime(self, diffs): diffs = ''.join(diffs) scratch = self.view.window().new_file() From d40d6b1615bfb2226b8f00038ab3361f77a43283 Mon Sep 17 00:00:00 2001 From: Ali Ayas Date: Wed, 27 May 2020 01:20:01 +0300 Subject: [PATCH 15/25] Use selection if any, for external tools This is already how it works for internal diff. This commit brings consistency. --- file_diffs.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/file_diffs.py b/file_diffs.py index 96cc591..d09d6a2 100644 --- a/file_diffs.py +++ b/file_diffs.py @@ -251,7 +251,11 @@ def update_view(self, view, edit, tmp_file): def file_will_be_read_from_disk(self, file): view = self.view.window().find_open_file(file) - return bool(os.path.exists(file) and not (view and view.is_dirty())) + return bool(os.path.exists(file) and not (view and view.is_dirty()) and not (view and self.view_has_a_selection(view))) + + def view_has_a_selection(self, view): + """Checks if it has exactly one non-empty selection.""" + return len(view.sel()) == 1 and not view.sel()[0].empty() class FileDiffDummy1Command(sublime_plugin.TextCommand): From 0e9c812d21e4552cfd93e3b7b4932932a5f76658 Mon Sep 17 00:00:00 2001 From: Ali Ayas Date: Mon, 25 May 2020 23:57:33 +0300 Subject: [PATCH 16/25] Reverse clipboard direction "reverse_clipboard" setting was introduced in 28ff059c2036c77b7186387b18a9e96702dde136 (see #85). With this commit, the reversed behaviour becomes default. "reverse_clipboard" option still exists to reverse the new behaviour. New behaviour is more consistent with the plugin. Such as: * "Diff with previous" in the plugin means "diff from previous to this". * And now "Diff with clipboard" means "diff from clipboard to this". --- FileDiffs.sublime-settings | 3 +++ file_diffs.py | 15 ++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/FileDiffs.sublime-settings b/FileDiffs.sublime-settings index 0d0ffc2..52bffc9 100644 --- a/FileDiffs.sublime-settings +++ b/FileDiffs.sublime-settings @@ -41,4 +41,7 @@ // "expand_full_file_name_in_tab": false // "apply_tempfile_changes_after_diff_tool": false // "limit": 1000 + + // Reverses "diff with clipboard" direction. + // "reverse_clipboard": true, } diff --git a/file_diffs.py b/file_diffs.py index d09d6a2..596b4ff 100644 --- a/file_diffs.py +++ b/file_diffs.py @@ -265,21 +265,22 @@ def run(self, edit, content): class FileDiffClipboardCommand(FileDiffCommand): def run(self, edit, **kwargs): - from_file = self.get_file_name(self.view, 'untitled') + to_file = self.get_file_name(self.view, 'untitled') for region in self.view.sel(): if not region.empty(): - from_file += ' (Selection)' + to_file += ' (Selection)' break clipboard = sublime.get_clipboard() def on_post_diff_tool(from_file, to_file): - self.update_view(self.view, edit, from_file) - sublime.set_clipboard(self.get_content_from_file(to_file)) + self.update_view(self.view, edit, to_file) + sublime.set_clipboard(self.get_content_from_file(from_file)) reverse = kwargs.get('reverse') or self.get_setting('reverse_clipboard', False) kwargs.update({'post_diff_tool': on_post_diff_tool, 'reverse': reverse}) - self.run_diff(self.diff_content(self.view), clipboard, - from_file=from_file, - to_file='(clipboard)', + + self.run_diff(clipboard, self.diff_content(self.view), + from_file='(clipboard)', + to_file=to_file, **kwargs) def is_visible(self): From 84fee0520365e6d458fdea2e817e27416bac1735 Mon Sep 17 00:00:00 2001 From: Ali Ayas Date: Fri, 29 May 2020 17:22:09 +0300 Subject: [PATCH 17/25] Code refactoring Co-authored-by: Colin T.A. Gray --- file_diffs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/file_diffs.py b/file_diffs.py index 596b4ff..f87e65b 100644 --- a/file_diffs.py +++ b/file_diffs.py @@ -251,7 +251,7 @@ def update_view(self, view, edit, tmp_file): def file_will_be_read_from_disk(self, file): view = self.view.window().find_open_file(file) - return bool(os.path.exists(file) and not (view and view.is_dirty()) and not (view and self.view_has_a_selection(view))) + return os.path.exists(file) and not (view and view.is_dirty()) and not (view and self.view_has_a_selection(view)) def view_has_a_selection(self, view): """Checks if it has exactly one non-empty selection.""" From a7f4ec558d23691cf37d26cf33e8af244d37eeb0 Mon Sep 17 00:00:00 2001 From: Ali Ayas Date: Thu, 24 Jun 2021 03:25:56 +0300 Subject: [PATCH 18/25] Implement setting for number of context lines --- FileDiffs.sublime-settings | 3 +++ file_diffs.py | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/FileDiffs.sublime-settings b/FileDiffs.sublime-settings index 52bffc9..bd8730c 100644 --- a/FileDiffs.sublime-settings +++ b/FileDiffs.sublime-settings @@ -44,4 +44,7 @@ // Reverses "diff with clipboard" direction. // "reverse_clipboard": true, + + // Number of context lines. Defaults to 3. For full context, set it as "full". + // "context_lines": 3, } diff --git a/file_diffs.py b/file_diffs.py index f87e65b..444ac36 100644 --- a/file_diffs.py +++ b/file_diffs.py @@ -1,6 +1,7 @@ # coding: utf8 import os import re +import sys import sublime import sublime_plugin @@ -96,7 +97,11 @@ def run_diff(self, a, b, from_file, to_file, **options): (from_content, from_file) = self.prep_content(a, from_file, 'from_file') (to_content, to_file) = self.prep_content(b, to_file, 'to_file') - diffs = list(difflib.unified_diff(from_content, to_content, from_file, to_file)) + context_lines = self.get_setting("context_lines", 3); + if context_lines == "full": + context_lines = sys.maxsize + + diffs = list(difflib.unified_diff(from_content, to_content, from_file, to_file, n=context_lines)) if not diffs: if sublime.version() < '3000': From dc156bb95c77e7aa00040991d3c485346f53c727 Mon Sep 17 00:00:00 2001 From: Ali Ayas Date: Thu, 24 Jun 2021 03:30:08 +0300 Subject: [PATCH 19/25] Don't use HTML popup for "No Difference" message It causes bad UX when the cursor is not visible in the view i.e. the view is scrolled to the opposite direction. In that case, the user doesn't notice the popup because it appears next to the unvisible cursor. --- file_diffs.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/file_diffs.py b/file_diffs.py index f87e65b..6f5b688 100644 --- a/file_diffs.py +++ b/file_diffs.py @@ -99,10 +99,8 @@ def run_diff(self, a, b, from_file, to_file, **options): diffs = list(difflib.unified_diff(from_content, to_content, from_file, to_file)) if not diffs: - if sublime.version() < '3000': - sublime.status_message('No Difference') - else: - self.view.show_popup('No Difference') + sublime.status_message('No Difference') + else: external_command = external_diff_tool or self.get_setting('cmd') open_in_sublime = self.get_setting('open_in_sublime', not external_command) From 207cc237f8434a27e8eb88c1d67c6d49fc0f711f Mon Sep 17 00:00:00 2001 From: "Colin T.A. Gray" Date: Wed, 1 Nov 2023 11:44:20 -0400 Subject: [PATCH 20/25] prefer "view.show_popup" over "sublime.status_message" for errors and alerts --- file_diffs.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/file_diffs.py b/file_diffs.py index 3e3fcf5..aa9bdd5 100644 --- a/file_diffs.py +++ b/file_diffs.py @@ -104,7 +104,7 @@ def run_diff(self, a, b, from_file, to_file, **options): diffs = list(difflib.unified_diff(from_content, to_content, from_file, to_file, n=context_lines)) if not diffs: - sublime.status_message('No Difference') + self.view.show_popup('No Difference') else: external_command = external_diff_tool or self.get_setting('cmd') @@ -197,7 +197,7 @@ def trim_trailing_white_space(file_name): post_diff_tool(from_file, to_file) except Exception as e: # some basic logging here, since we are cluttering the /tmp folder - sublime.status_message(str(e)) + self.view.show_popup(str(e)) finally: def remove_files(): @@ -248,7 +248,7 @@ def update_view(self, view, edit, tmp_file): elif nb_non_empty_regions == 1: region = non_empty_regions[0] else: - sublime.status_message('Cannot update multiselection') + self.view.show_popup('Cannot update multiselection') return view.replace(edit, region, self.get_content_from_file(tmp_file)) @@ -402,7 +402,7 @@ def find_files(self, folders, ret=[]): if not len([True for pattern in file_exclude_patterns if fnmatch(f, pattern)]): ret.append(fullpath) if len(ret) >= max_files: - sublime.status_message('Too many files to include all of them in this list') + self.view.show_popup('Too many files to include all of them in this list') return ret return ret From 48f5e5ecadbff8c5340ad029fdd7dab7141b360a Mon Sep 17 00:00:00 2001 From: Glenn 'devalias' Grant Date: Wed, 10 Jan 2024 11:28:55 +1100 Subject: [PATCH 21/25] update Main.sublime-menu to use edit_settings for side-by-side settings --- Main.sublime-menu | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Main.sublime-menu b/Main.sublime-menu index edb3be8..a1d1d28 100644 --- a/Main.sublime-menu +++ b/Main.sublime-menu @@ -15,8 +15,14 @@ "caption": "FileDiffs", "children": [ - { "command": "open_file", "args": {"file": "${packages}/FileDiffs/FileDiffs.sublime-settings"}, "caption": "Settings – Default" }, - { "command": "open_file", "args": {"file": "${packages}/User/FileDiffs.sublime-settings"}, "caption": "Settings – User" }, + { + "command": "edit_settings", + "args": { + "base_file": "${packages}/FileDiffs/FileDiffs.sublime-settings", + "default": "{\n$0\n}\n" + }, + "caption": "Settings" + }, { "caption": "-" } ] } From 66c8a2f0478037f06056d6e1f521ebd31a201427 Mon Sep 17 00:00:00 2001 From: "Colin T.A. Gray" Date: Wed, 19 Feb 2025 12:14:55 -0500 Subject: [PATCH 22/25] package.json schema updated to 4.0 --- package.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 06c997b..8c9ff7a 100644 --- a/package.json +++ b/package.json @@ -3,10 +3,12 @@ "description": "Shows diffs between the current file, or selection(s) in the current file, and clipboard, another file, or unsaved changes. With contributions from Sebastian Pape (spape) and Jiri Urban (jiriurban)", "author": "Colin T.A. Gray (colinta)", "details": "https://github.com/colinta/SublimeFileDiffs", - "labels": ["diff/merge"], + "labels": [ + "diff/merge" + ], "releases": [ { - "details": "https://github.com/colinta/SublimeFileDiffs/tags", + "tags": true, "sublime_text": ">=3000" } ] From cb1cbc3748ecf486532e50e777da7cb3f2380965 Mon Sep 17 00:00:00 2001 From: "Colin T.A. Gray" Date: Sat, 29 Mar 2025 07:13:38 -0400 Subject: [PATCH 23/25] adds messages.json and documentation for key bindings --- README.md | 52 ++++++++++++++------------------------------------- messages.json | 3 +++ 2 files changed, 17 insertions(+), 38 deletions(-) create mode 100644 messages.json diff --git a/README.md b/README.md index 4f2abfb..614cdd2 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ FileDiffs Plugin Shows diffs between the current file, or selection(s) in the current file, and clipboard, another file, or unsaved changes. Can be configured to show diffs in an external diff tool -## Preview +### Preview ![Preview](https://github.com/ildarkhasanshin/SublimeFileDiffs/raw/master/preview_1.png) @@ -23,43 +23,10 @@ Check the [wiki][] for more tips Installation ------------ -### Package Control (Easiest) +Using Package Control, install `FileDiffs` or clone this repo in your packages folder. -1. Using Package Control, install `FileDiffs` - -2. Install keymaps for the commands (see Example.sublime-keymap for my preferred keys) - -### Sublime Text 3 - -1. Open the Sublime Text Packages folder - - OS X: `~/Library/Application Support/Sublime Text 3/Packages/` - - Windows: `%APPDATA%/Sublime Text 3/Packages/` - - Linux: `~/.Sublime Text 3/Packages/` or `~/.config/sublime-text-3/Packages` - -2. Clone this repo - - ``` - # Over SSH - git clone git@github.com:colinta/SublimeFileDiffs - - # Over HTTPS - git clone https://github.com/colinta/SublimeFileDiffs.git - ``` - -3. Install keymaps for the commands (see Example.sublime-keymap for my preferred keys) - -### Sublime Text 2 - -1. Open the Sublime Text 2 Packages folder -2. Clone this repo, but use the `st2` branch - - ``` - # Over SSH - git clone -b st2 git@github.com:colinta/SublimeFileDiffs - - # Over HTTPS - git clone -b st2 https://github.com/colinta/SublimeFileDiffs.git - ``` +I recommended you add key bindings for the commands. I've included my preferred bindings below. +Copy them to your key bindings file (⌘⇧,). Add External Diff Tool *(optional)* -------- @@ -77,7 +44,6 @@ It supports: - A generic setting `FileDiffs.sublime-settings` which could be overloaded for each parameter in a platform specific configuration `FileDiffs ($platform).sublime-settings` in the `Settings - User` - Environment variable expansions for `cmd` parameter in the settings - Commands -------- @@ -99,6 +65,16 @@ The rest of the commands do not need to be bound (accessible from the menu): If FileDiffs has to use temporary files, they are created in your `Data/Packages` folder (rather than system temp folder) due to privacy concerns for portable Sublime Text installations. Temporary files are automatically removed after 15 seconds. +Key Bindings +------------ + +Copy these to your user key bindings file. + + + { "keys": ["ctrl+shift+d"], "command": "file_diff_menu" }, + { "keys": ["ctrl+shift+e"], "command": "file_diff_menu", "args": {"cmd": ["opendiff", "$file1", "$file2"] } }, + + Contributors ------------ diff --git a/messages.json b/messages.json new file mode 100644 index 0000000..7b20f7b --- /dev/null +++ b/messages.json @@ -0,0 +1,3 @@ +{ + "install": "README.md" +} From d53ad902ff505e61545b7c18a97aac6d07e5a99e Mon Sep 17 00:00:00 2001 From: "Colin T.A. Gray" Date: Sun, 13 Apr 2025 09:36:02 -0400 Subject: [PATCH 24/25] rm messages.json - broken maybe --- messages.json | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 messages.json diff --git a/messages.json b/messages.json deleted file mode 100644 index 7b20f7b..0000000 --- a/messages.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "install": "README.md" -} From cb5215ad31e01aef3029878893542ec1a07d4a3d Mon Sep 17 00:00:00 2001 From: "Colin T.A. Gray" Date: Sun, 13 Apr 2025 10:52:51 -0400 Subject: [PATCH 25/25] final formatting of package.json --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 8c9ff7a..5f336bf 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,15 @@ { "name": "FileDiffs", + "details": "https://github.com/colinta/SublimeFileDiffs", "description": "Shows diffs between the current file, or selection(s) in the current file, and clipboard, another file, or unsaved changes. With contributions from Sebastian Pape (spape) and Jiri Urban (jiriurban)", "author": "Colin T.A. Gray (colinta)", - "details": "https://github.com/colinta/SublimeFileDiffs", "labels": [ "diff/merge" ], "releases": [ { - "tags": true, - "sublime_text": ">=3000" + "sublime_text": ">=3000", + "tags": true } ] }