Merge pull request #165 from hugoholgersson/mac_os_port

Port of Diffuse to macOS
This commit is contained in:
Creak 2022-10-23 16:48:02 -04:00 committed by GitHub
commit 5583ca03d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 283 additions and 99 deletions

6
.markdownlint.json Normal file
View File

@ -0,0 +1,6 @@
{
"default": true,
"no-duplicate-heading": false,
"no-inline-html": false,
"no-emphasis-as-heading": false
}

BIN
data/icons/diffuse.icns Normal file

Binary file not shown.

View File

@ -11,3 +11,7 @@ install_data(
join_paths(symbolic_dir, ('@0@-symbolic.svg').format(application_id)),
install_dir: join_paths(get_option('datadir'), 'icons', symbolic_dir)
)
if build_machine.system() == 'darwin'
install_data('diffuse.icns', install_dir: '/Applications/Diffuse.app/Contents/Resources')
endif

View File

@ -1,34 +1,36 @@
pkgdatadir = join_paths(get_option('prefix'), get_option('datadir'), meson.project_name())
desktop_file = i18n.merge_file(
if build_machine.system() == 'linux'
desktop_file = i18n.merge_file(
input: 'io.github.mightycreak.Diffuse.desktop.in',
output: 'io.github.mightycreak.Diffuse.desktop',
type: 'desktop',
po_dir: '../po',
install: true,
install_dir: join_paths(get_option('datadir'), 'applications')
)
)
desktop_utils = find_program('desktop-file-validate', required: false)
if desktop_utils.found()
desktop_utils = find_program('desktop-file-validate', required: false)
if desktop_utils.found()
test('Validate desktop file', desktop_utils,
args: [desktop_file]
)
endif
endif
appstream_file = i18n.merge_file(
appstream_file = i18n.merge_file(
input: 'io.github.mightycreak.Diffuse.appdata.xml.in',
output: 'io.github.mightycreak.Diffuse.appdata.xml',
po_dir: '../po',
install: true,
install_dir: join_paths(get_option('datadir'), 'appdata')
)
)
appstream_util = find_program('appstream-util', required: false)
if appstream_util.found()
appstream_util = find_program('appstream-util', required: false)
if appstream_util.found()
test('Validate appstream file', appstream_util,
args: ['validate', appstream_file]
)
endif
endif
# Diffuse config file

View File

@ -8,13 +8,14 @@ Diffuse depends on these projects:
* PyPi
* Cairo and GObject Introspection development headers
* Meson
* Flatpak and Flatpak builder
* Flatpak and Flatpak builder (Linux only)
### Install the distribution dependencies
## Install the dependencies
It's a bit difficult to get the command lines for all the distributions and
their releases, but it should be enough to find the packages on other
distributions.
### Install the system dependencies
It's a bit difficult to get the command lines for all the systems, but these
examples should be enough to find the packages on most systems.
<details>
<summary>Debian/Ubuntu</summary>
@ -32,7 +33,19 @@ _Note: Tested on Debian 11 (Buster) and Ubuntu 20.04 (Focal)_
sudo dnf install python-pip cairo-devel cairo-gobject-devel meson flatpak flatpak-builder
```
_Note: Tested on Fedora 34_
_Note: Tested on Fedora 36_
</details>
<details>
<summary>Mac OS</summary>
On Mac, all dependencies can be installed using [Homebrew](https://docs.brew.sh/):
```sh
brew install meson python3 py3cairo pygobject3 gtk+3 librsvg
```
_Note: Tested on macOS 12.5 (Monterey)_
</details>
### Install the project dependencies
@ -49,34 +62,42 @@ For developer tools, run this one instead (it includes requirements.txt):
pip install -r requirements.dev.txt
```
## Setup on Linux
## Setup
### Build, test and install using Flatpak
### Setup on Linux using Flatpak
To install Diffuse locally:
#### Build, test and install
To build, test and install Diffuse locally:
```sh
flatpak install runtime/org.gnome.Sdk/$(uname -p)/42
flatpak-builder --user --install build-flatpak io.github.mightycreak.Diffuse.yml
```
#### Run
To run Diffuse through Flatpak:
```sh
flatpak run io.github.mightycreak.Diffuse
```
#### Uninstall
To uninstall Diffuse:
```sh
flatpak remove io.github.mightycreak.Diffuse
```
### Build, test and install using Meson
### Setup on Linux using Meson
Diffuse build system is meson.
#### Build and test
To compile and test Diffuse:
Diffuse is using Meson as its build system.
To build and test Diffuse:
```sh
meson setup build
@ -85,36 +106,81 @@ meson compile
meson test
```
#### Install on system and run
To install Diffuse on your system (e.g. `/usr/local/`):
```sh
meson install # requires admin privileges
```
# Run Diffuse
To run Diffuse:
```sh
diffuse
```
To install Diffuse on a custom directory (e.g. `~/bin/diffuse`):
#### Install in a custom directory and run
Meson allows to change the default installation directories, see
[command-line documentation](https://mesonbuild.com/Commands.html#configure).
To install Diffuse in a custom directory (e.g. `~/bin/diffuse`):
```sh
meson install --destdir ~/bin/diffuse
# Run Diffuse
cd ~/bin/diffuse/usr/local/bin
PYTHONPATH=$HOME/bin/diffuse/usr/local/share/diffuse ./diffuse
```
To uninstall diffuse afterwards:
To run Diffuse:
```sh
export PYTHONPATH=$HOME/bin/diffuse/usr/local/share/diffuse
cd ~/bin/diffuse/usr/local/bin
./diffuse
```
#### Uninstall
To uninstall Diffuse afterwards:
```sh
sudo ninja uninstall -C build
sudo rm -v /usr/local/share/locale/*/LC_MESSAGES/diffuse.mo
```
Meson allows to change the default installation directories, see
[command-line documentation](https://mesonbuild.com/Commands.html#configure).
### Setup on Mac OS
## Setup on Windows
#### Build and test
Diffuse is using Meson as its build system.
To build and test Diffuse:
```sh
meson setup build
cd build
meson compile
meson test
```
#### Install on system and run
To install Diffuse on your system (e.g. `/usr/local/`):
```sh
meson install # requires admin privileges
```
To run Diffuse:
```sh
diffuse
```
_Note: The `diffuse` command can be used to launch Diffuse as a native Mac app_
_that is installed into `/Applications/Diffuse.app`._
### Setup on Windows (deprecated)
_Note:_ The Windows port is not maintained and would need some love.
Contributions are very welcome! 😉

View File

@ -19,16 +19,16 @@ few manual steps.
- data/usr/share/gnome/help/diffuse/*/diffuse.xml
- data/usr/share/omf/diffuse/diffuse-*.omf
- utils/book2manual.xsl
3. Update CHANGELOG.md
4. Update CHANGELOG.md
- Add new line under `## Unreleased` following this syntax: `## x.y.z - YYYY-MM-DD`
- Copy the content of the changes for this release
4. Update AppData release notes in data/io.github.mightycreak.Diffuse.appdata.xml.in:
5. Update AppData release notes in data/io.github.mightycreak.Diffuse.appdata.xml.in:
- Create a new `<release>` tag under `<releases>`, fill the `version` and
`date` attributes
- Create a new `<description>` tag under the new `<release>` tag
- Add one paragraph to sum the release in one sentence (e.g. highlights, ...)
- Paste the changes from the changelog and adapt it to HTML
5. Create new branch and PR
6. Create new branch and PR
## Create new release on GitHub

View File

@ -70,6 +70,7 @@ Use `update-translations.py` to validate one or more PO files.
Here is an example with `ja.po` and `ko.po`:
Command-line:
```sh
./update-translations.py --check-only ja.po ko.po
```
@ -99,8 +100,10 @@ POSIX platforms. The conversion tools insert some English text that gets
localized using search and replace. Manually add new search and replace rules
to these files:
../windows-installer/translations.txt
../utils/translations.txt
```text
../windows-installer/translations.txt
../utils/translations.txt
```
The format of each line is: \<language id\>:\<English text\>:\<localised text\>

View File

@ -22,3 +22,12 @@ The Diffuse package statuses for every distributions can been seen on
Huge thanks to [@bongochong](https://github.com/bongochong) for maintaining the
Diffuse package on Fedora.
## Integrate with Git
Diffuse is compatible with `git difftool` command-line. To use Diffuse as Git
diff tool, run:
```sh
git config --global diff.tool diffuse
```

View File

@ -10,4 +10,6 @@ subdir('data')
subdir('src')
subdir('po')
meson.add_install_script('build-aux/meson/postinstall.py')
if build_machine.system() == 'linux'
meson.add_install_script('build-aux/meson/postinstall.py')
endif

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>mac_launcher.sh</string>
<key>CFBundleIconFile</key>
<string>diffuse.icns</string>
<key>CFBundleIdentifier</key>
<string>io.github.mightycreak.Diffuse</string>
<key>CFBundleName</key>
<string>Diffuse</string>
<key>CFBundleShortVersionString</key>
<string>@VERSION@</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>NSAppleScriptEnabled</key>
<string>NO</string>
</dict>
</plist>

View File

@ -0,0 +1,14 @@
#!/bin/bash
#
# This is the command-line entry point on Mac.
#
# We want to use Mac's `open` command for mainly two reasons;
# a) open lets us choose our own icon.
# b) open puts the app on top of the other windows, including the terminal we ran this from.
#
# --new lets us open multiple windows.
# --wait-apps lets Diffuse be a "git difftool", letting Diffuse run before git deletes its tmp files.
#
# We pass "pwd" because Mac's `open` command launches processes at '/'.
# "printf %q" escapes spaces and other characters so the complete dir is passed as one.
open /Applications/Diffuse.app --new --wait-apps --args $(printf %q "$(pwd)") $@

View File

@ -0,0 +1,7 @@
#!/bin/bash
# Mac's `open` command resets working dir. This extra script only
# does `cd` back to the dir from which `diffuse` was launched to
# allow Python to pick up any relative paths given by `git difftool`.
cd $1
@BINDIR@/diffuse_impl ${@:2}

View File

@ -9,15 +9,40 @@ conf.set('VERSION', meson.project_version())
conf.set('PKGDATADIR', pkgdatadir)
conf.set('LOCALEDIR', join_paths(get_option('prefix'), get_option('localedir')))
conf.set('SYSCONFIGDIR', join_paths(get_option('prefix'), get_option('sysconfdir')))
conf.set('BINDIR', join_paths(get_option('prefix'), get_option('bindir')))
configure_file(
input: 'diffuse.in',
output: 'diffuse',
output: build_machine.system() == 'darwin' ? 'diffuse_impl' : 'diffuse',
configuration: conf,
install: true,
install_dir: get_option('bindir')
)
if build_machine.system() == 'darwin'
configure_file(
input: 'mac-os-app/diffuse-mac.in',
output: 'diffuse',
configuration: conf,
install: true,
install_dir: get_option('bindir')
)
configure_file(
input: 'mac-os-app/mac_launcher.sh.in',
output: 'mac_launcher.sh',
configuration: conf,
install: true,
install_dir: '/Applications/Diffuse.app/Contents/MacOS'
)
configure_file(
input: 'mac-os-app/Info.plist.in',
output: 'Info.plist',
configuration: conf,
install: true,
install_dir: '/Applications/Diffuse.app/Contents'
)
endif
diffuse_sources = [
'__init__.py',
'constants.py',

View File

@ -26,6 +26,7 @@
import glob
import os
import platform
import re
import shlex
@ -43,36 +44,37 @@ from gi.repository import Gdk # type: ignore # noqa: E402
class Resources:
def __init__(self):
# default keybindings
defaultModKey = 'Cmd+' if platform.system() == 'Darwin' else 'Ctrl+'
self.keybindings = {}
self.keybindings_lookup = {}
self.setKeyBinding('menu', 'open_file', 'Ctrl+o')
self.setKeyBinding('menu', 'open_file_in_new_tab', 'Ctrl+t')
self.setKeyBinding('menu', 'open_file', defaultModKey + 'o')
self.setKeyBinding('menu', 'open_file_in_new_tab', defaultModKey + 't')
self.setKeyBinding('menu', 'open_modified_files', 'Shift+Ctrl+O')
self.setKeyBinding('menu', 'open_commit', 'Shift+Ctrl+T')
self.setKeyBinding('menu', 'reload_file', 'Shift+Ctrl+R')
self.setKeyBinding('menu', 'save_file', 'Ctrl+s')
self.setKeyBinding('menu', 'save_file_as', 'Shift+Ctrl+A')
self.setKeyBinding('menu', 'save_all', 'Shift+Ctrl+S')
self.setKeyBinding('menu', 'save_file', defaultModKey + 's')
self.setKeyBinding('menu', 'save_file_as', defaultModKey + 'Shift+A')
self.setKeyBinding('menu', 'save_all', defaultModKey + 'Shift+S')
self.setKeyBinding('menu', 'new_2_way_file_merge', 'Ctrl+2')
self.setKeyBinding('menu', 'new_3_way_file_merge', 'Ctrl+3')
self.setKeyBinding('menu', 'new_n_way_file_merge', 'Ctrl+4')
self.setKeyBinding('menu', 'close_tab', 'Ctrl+w')
self.setKeyBinding('menu', 'undo_close_tab', 'Shift+Ctrl+W')
self.setKeyBinding('menu', 'quit', 'Ctrl+q')
self.setKeyBinding('menu', 'undo', 'Ctrl+z')
self.setKeyBinding('menu', 'redo', 'Shift+Ctrl+Z')
self.setKeyBinding('menu', 'cut', 'Ctrl+x')
self.setKeyBinding('menu', 'copy', 'Ctrl+c')
self.setKeyBinding('menu', 'paste', 'Ctrl+v')
self.setKeyBinding('menu', 'select_all', 'Ctrl+a')
self.setKeyBinding('menu', 'clear_edits', 'Ctrl+r')
self.setKeyBinding('menu', 'dismiss_all_edits', 'Ctrl+d')
self.setKeyBinding('menu', 'find', 'Ctrl+f')
self.setKeyBinding('menu', 'find_next', 'Ctrl+g')
self.setKeyBinding('menu', 'find_previous', 'Shift+Ctrl+G')
self.setKeyBinding('menu', 'go_to_line', 'Shift+Ctrl+L')
self.setKeyBinding('menu', 'realign_all', 'Ctrl+l')
self.setKeyBinding('menu', 'isolate', 'Ctrl+i')
self.setKeyBinding('menu', 'close_tab', defaultModKey + 'w')
self.setKeyBinding('menu', 'undo_close_tab', defaultModKey + 'Shift+W')
self.setKeyBinding('menu', 'quit', defaultModKey + 'q')
self.setKeyBinding('menu', 'undo', defaultModKey + 'z')
self.setKeyBinding('menu', 'redo', defaultModKey + 'Shift+Z')
self.setKeyBinding('menu', 'cut', defaultModKey + 'x')
self.setKeyBinding('menu', 'copy', defaultModKey + 'c')
self.setKeyBinding('menu', 'paste', defaultModKey + 'v')
self.setKeyBinding('menu', 'select_all', defaultModKey + 'a')
self.setKeyBinding('menu', 'clear_edits', defaultModKey + 'r')
self.setKeyBinding('menu', 'dismiss_all_edits', defaultModKey + 'd')
self.setKeyBinding('menu', 'find', defaultModKey + 'f')
self.setKeyBinding('menu', 'find_next', defaultModKey + 'g')
self.setKeyBinding('menu', 'find_previous', defaultModKey + 'Shift+G')
self.setKeyBinding('menu', 'go_to_line', defaultModKey + 'Shift+l')
self.setKeyBinding('menu', 'realign_all', defaultModKey + 'l')
self.setKeyBinding('menu', 'isolate', defaultModKey + 'i')
self.setKeyBinding('menu', 'first_difference', 'Shift+Ctrl+Up')
self.setKeyBinding('menu', 'previous_difference', 'Ctrl+Up')
self.setKeyBinding('menu', 'next_difference', 'Ctrl+Down')
@ -83,24 +85,24 @@ class Resources:
self.setKeyBinding('menu', 'last_tab', 'Shift+Ctrl+Page_Down')
self.setKeyBinding('menu', 'shift_pane_right', 'Shift+Ctrl+parenright')
self.setKeyBinding('menu', 'shift_pane_left', 'Shift+Ctrl+parenleft')
self.setKeyBinding('menu', 'convert_to_upper_case', 'Ctrl+u')
self.setKeyBinding('menu', 'convert_to_lower_case', 'Shift+Ctrl+U')
self.setKeyBinding('menu', 'sort_lines_in_ascending_order', 'Ctrl+y')
self.setKeyBinding('menu', 'sort_lines_in_descending_order', 'Shift+Ctrl+Y')
self.setKeyBinding('menu', 'remove_trailing_white_space', 'Ctrl+k')
self.setKeyBinding('menu', 'convert_tabs_to_spaces', 'Ctrl+b')
self.setKeyBinding('menu', 'convert_to_upper_case', defaultModKey + 'u')
self.setKeyBinding('menu', 'convert_to_lower_case', defaultModKey + 'Shift+U')
self.setKeyBinding('menu', 'sort_lines_in_ascending_order', defaultModKey + 'y')
self.setKeyBinding('menu', 'sort_lines_in_descending_order', defaultModKey + 'Shift+Y')
self.setKeyBinding('menu', 'remove_trailing_white_space', defaultModKey + 'k')
self.setKeyBinding('menu', 'convert_tabs_to_spaces', defaultModKey + 'b')
self.setKeyBinding('menu', 'convert_leading_spaces_to_tabs', 'Shift+Ctrl+B')
self.setKeyBinding('menu', 'increase_indenting', 'Shift+Ctrl+greater')
self.setKeyBinding('menu', 'decrease_indenting', 'Shift+Ctrl+less')
self.setKeyBinding('menu', 'convert_to_dos', 'Shift+Ctrl+E')
self.setKeyBinding('menu', 'convert_to_mac', 'Shift+Ctrl+C')
self.setKeyBinding('menu', 'convert_to_unix', 'Ctrl+e')
self.setKeyBinding('menu', 'copy_selection_right', 'Shift+Ctrl+Right')
self.setKeyBinding('menu', 'copy_selection_left', 'Shift+Ctrl+Left')
self.setKeyBinding('menu', 'copy_left_into_selection', 'Ctrl+Right')
self.setKeyBinding('menu', 'copy_right_into_selection', 'Ctrl+Left')
self.setKeyBinding('menu', 'merge_from_left_then_right', 'Ctrl+m')
self.setKeyBinding('menu', 'merge_from_right_then_left', 'Shift+Ctrl+M')
self.setKeyBinding('menu', 'increase_indenting', defaultModKey + 'Shift+greater')
self.setKeyBinding('menu', 'decrease_indenting', defaultModKey + 'Shift+less')
self.setKeyBinding('menu', 'convert_to_dos', defaultModKey + 'Shift+E')
self.setKeyBinding('menu', 'convert_to_mac', defaultModKey + 'Shift+C')
self.setKeyBinding('menu', 'convert_to_unix', defaultModKey + 'e')
self.setKeyBinding('menu', 'copy_selection_right', defaultModKey + 'Shift+Right')
self.setKeyBinding('menu', 'copy_selection_left', defaultModKey + 'Shift+Left')
self.setKeyBinding('menu', 'copy_left_into_selection', defaultModKey + 'Right')
self.setKeyBinding('menu', 'copy_right_into_selection', defaultModKey + 'Left')
self.setKeyBinding('menu', 'merge_from_left_then_right', defaultModKey + 'm')
self.setKeyBinding('menu', 'merge_from_right_then_left', defaultModKey + 'Shift+M')
self.setKeyBinding('menu', 'help_contents', 'F1')
self.setKeyBinding('line_mode', 'enter_align_mode', 'space')
self.setKeyBinding('line_mode', 'enter_character_mode', 'Return')
@ -126,13 +128,13 @@ class Resources:
self.setKeyBinding('line_mode', 'right', 'l')
self.setKeyBinding('line_mode', 'extend_right', 'Shift+Right')
self.setKeyBinding('line_mode', 'page_up', 'Page_Up')
self.setKeyBinding('line_mode', 'page_up', 'Ctrl+u')
self.setKeyBinding('line_mode', 'page_up', defaultModKey + 'u')
self.setKeyBinding('line_mode', 'extend_page_up', 'Shift+Page_Up')
self.setKeyBinding('line_mode', 'extend_page_up', 'Shift+Ctrl+U')
self.setKeyBinding('line_mode', 'extend_page_up', defaultModKey + 'Shift+U')
self.setKeyBinding('line_mode', 'page_down', 'Page_Down')
self.setKeyBinding('line_mode', 'page_down', 'Ctrl+d')
self.setKeyBinding('line_mode', 'page_down', defaultModKey + 'd')
self.setKeyBinding('line_mode', 'extend_page_down', 'Shift+Page_Down')
self.setKeyBinding('line_mode', 'extend_page_down', 'Shift+Ctrl+D')
self.setKeyBinding('line_mode', 'extend_page_down', defaultModKey + 'Shift+D')
self.setKeyBinding('line_mode', 'delete_text', 'BackSpace')
self.setKeyBinding('line_mode', 'delete_text', 'Delete')
self.setKeyBinding('line_mode', 'delete_text', 'x')
@ -165,9 +167,9 @@ class Resources:
self.setKeyBinding('align_mode', 'right', 'Right')
self.setKeyBinding('align_mode', 'right', 'l')
self.setKeyBinding('align_mode', 'page_up', 'Page_Up')
self.setKeyBinding('align_mode', 'page_up', 'Ctrl+u')
self.setKeyBinding('align_mode', 'page_up', defaultModKey + 'u')
self.setKeyBinding('align_mode', 'page_down', 'Page_Down')
self.setKeyBinding('align_mode', 'page_down', 'Ctrl+d')
self.setKeyBinding('align_mode', 'page_down', defaultModKey + 'd')
self.setKeyBinding('character_mode', 'enter_line_mode', 'Escape')
# default colours
@ -232,6 +234,8 @@ class Resources:
modifiers |= Gdk.ModifierType.SHIFT_MASK
elif token == 'Ctrl':
modifiers |= Gdk.ModifierType.CONTROL_MASK
elif token == 'Cmd':
modifiers |= Gdk.ModifierType.META_MASK
elif token == 'Alt':
modifiers |= Gdk.ModifierType.MOD1_MASK
elif len(token) == 0 or token[0] == '_':

20
utils/makemacicon.sh Executable file
View File

@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Use this tool if you need to re-create
# Diffuse.app/Contents/Resources/diffuse.icns
# in case the icon changes (unlikely).
sizes=(16 32 64 128 256 512)
for s in "${sizes[@]}"; do
echo $s
rsvg-convert -h $s "$1" > "icon_${s}x$s.png"
done
cp 'icon_32x32.png' 'icon_16x16@2x.png'
cp 'icon_64x64.png' 'icon_32x32@2x.png'
cp 'icon_256x256.png' 'icon_128x128@2x.png'
cp 'icon_512x512.png' 'icon_256x256@2x.png'
mkdir icon.iconset
mv icon_*x*.png icon.iconset
iconutil -c icns icon.iconset