#lang racket/base (require racket/path "config.rkt") (provide find-mergetool find-mergetool-path configured-mergetool set-mergetool!) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Supporting functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; goal : Build a path below an environment variable when it is defined. ; pre : variable is an environment variable name. ; post : The environment has only been inspected. ; result : The constructed path, or #f. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (environment-path variable . parts) (let ((base (getenv variable))) (if base (apply build-path base parts) #f))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; goal : Find one merge tool candidate on PATH or at a well-known path. ; pre : candidate contains tool name, executable name and zero or more paths. ; post : PATH and the filesystem have only been inspected. ; result : A list containing tool name and executable path, or #f. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (find-candidate candidate) (let* ((tool (car candidate)) (executable (cadr candidate)) (path-executable (find-executable-path executable))) (cond (path-executable (list tool path-executable)) (else (let loop ((paths (cddr candidate))) (cond ((null? paths) #f) ((and (car paths) (file-exists? (car paths))) (list tool (car paths))) (else (loop (cdr paths))))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; goal : Find the first usable merge tool candidate. ; pre : candidates contains merge tool candidate descriptions. ; post : PATH and the filesystem have only been inspected. ; result : A list containing tool name and executable path, or #f. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (find-candidates candidates) (cond ((null? candidates) #f) (else (let ((candidate (find-candidate (car candidates)))) (if candidate candidate (find-candidates (cdr candidates))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; goal : Return well-known graphical merge tool candidates for Windows. ; pre : Windows environment variables may or may not be defined. ; post : The environment has only been inspected. ; result : Merge tool candidate descriptions in preference order. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (windows-mergetool-candidates) (list (list "winmerge" "WinMergeU.exe" (environment-path "ProgramFiles" "WinMerge" "WinMergeU.exe") (environment-path "ProgramFiles(x86)" "WinMerge" "WinMergeU.exe")) (list "vscode" "code.cmd" (environment-path "LOCALAPPDATA" "Programs" "Microsoft VS Code" "bin" "code.cmd") (environment-path "ProgramFiles" "Microsoft VS Code" "bin" "code.cmd") (environment-path "ProgramFiles(x86)" "Microsoft VS Code" "bin" "code.cmd")) (list "vscode" "code.exe" (environment-path "LOCALAPPDATA" "Programs" "Microsoft VS Code" "Code.exe") (environment-path "ProgramFiles" "Microsoft VS Code" "Code.exe") (environment-path "ProgramFiles(x86)" "Microsoft VS Code" "Code.exe")) (list "kdiff3" "kdiff3.exe" (environment-path "ProgramFiles" "KDiff3" "kdiff3.exe") (environment-path "ProgramFiles(x86)" "KDiff3" "kdiff3.exe")) (list "meld" "meld.exe" (environment-path "LOCALAPPDATA" "Programs" "Meld" "Meld.exe") (environment-path "ProgramFiles" "Meld" "Meld.exe") (environment-path "ProgramFiles(x86)" "Meld" "Meld.exe")) (list "tortoisemerge" "TortoiseMerge.exe" (environment-path "ProgramFiles" "TortoiseSVN" "bin" "TortoiseMerge.exe") (environment-path "ProgramFiles(x86)" "TortoiseSVN" "bin" "TortoiseMerge.exe")))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; goal : Return well-known graphical merge tool candidates for macOS. ; pre : The current platform is macOS. ; post : No program has been started. ; result : Merge tool candidate descriptions in preference order. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (macos-mergetool-candidates) (list (list "opendiff" "opendiff" (string->path "/usr/bin/opendiff")) (list "vscode" "code" (string->path "/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code")) (list "kdiff3" "kdiff3" (string->path "/Applications/kdiff3.app/Contents/MacOS/kdiff3")) (list "meld" "meld"))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; goal : Return well-known graphical merge tool candidates for Unix/Linux. ; pre : The current platform is Unix. ; post : No program has been started. ; result : Merge tool candidate descriptions in preference order. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (unix-mergetool-candidates) (list (list "meld" "meld" (string->path "/usr/bin/meld") (string->path "/usr/local/bin/meld")) (list "kdiff3" "kdiff3" (string->path "/usr/bin/kdiff3") (string->path "/usr/local/bin/kdiff3")) (list "vscode" "code" (string->path "/snap/bin/code") (string->path "/usr/bin/code") (string->path "/usr/local/bin/code")))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; goal : Return merge tool candidates for the current platform. ; pre : The current platform is supported by Racket. ; post : No program has been started. ; result : Merge tool candidate descriptions. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (mergetool-candidates) (case (system-type 'os) ((windows) (windows-mergetool-candidates)) ((macosx) (macos-mergetool-candidates)) ((unix) (unix-mergetool-candidates)) (else '()))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; goal : Find the automatically detected merge tool and executable path. ; pre : Platform paths are accessible. ; post : No merge tool has been started. ; result : A list containing tool name and path, or #f. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (detected-mergetool) (find-candidates (mergetool-candidates))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Provided functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; goal : Read the merge tool explicitly configured for git-cli. ; pre : The git-cli configuration is readable. ; post : The configuration has not been changed. ; result : The configured Git merge tool name, or #f. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (configured-mergetool) (cfg-get 'git 'mergetool #f)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; goal : Store the Git merge tool name used by git-cli. ; pre : tool is a Git mergetool name such as "winmerge" or "meld". ; post : The tool name has been stored in the git-cli configuration. ; result : The result returned by the configuration layer. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (set-mergetool! tool) (cfg-set! 'git 'mergetool tool)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; goal : Find the graphical merge tool that git-cli should prefer. ; pre : The platform and git-cli configuration are available. ; post : No merge tool has been started. ; result : A configured or detected Git merge tool name, or #f. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (find-mergetool) (or (configured-mergetool) (let ((detected (detected-mergetool))) (if detected (car detected) #f)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; goal : Find the executable path belonging to the automatically detected merge tool. ; pre : Platform paths are accessible. ; post : No merge tool has been started. ; result : The executable path, or #f. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (find-mergetool-path) (let ((configured (configured-mergetool)) (detected (detected-mergetool))) (if configured #f (if detected (cadr detected) #f))))