Files
git-cli/private/find-mergetool.rkt
T

202 lines
8.5 KiB
Racket

#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))))