Files

274 lines
11 KiB
Racket

#lang racket/base
(require racket/path
racket/string
"config.rkt")
(provide find-editor
find-editors
editor-downloads
configured-editor
set-editor!
set-editor-auto!)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Supporting functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Quote an executable path for use as a Git editor command.
; pre : p is a path to an executable.
; post : p has only been converted to a string.
; result : A quoted command path.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (quote-command-path p)
(format "\"~a\"" (path->string p)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 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 : Make an editor description from an executable path.
; pre : p is a path or #f; arguments contains any required wait arguments.
; post : The filesystem has only been inspected.
; result : (name description command), or #f when the executable does not exist.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (editor-at name description p arguments)
(if (and p (file-exists? p))
(list name
description
(string-append (quote-command-path p) arguments))
#f))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Find an editor executable on PATH.
; pre : executable is a pathless executable name.
; post : PATH has only been inspected.
; result : (name description command), or #f.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (editor-on-path name description executable arguments)
(let ((p (find-executable-path executable)))
(editor-at name description p arguments)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Remove duplicate editor descriptions while preserving preference order.
; pre : editors contains editor descriptions or #f values.
; post : editors has only been inspected.
; result : One editor description per editor name.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (unique-editors editors)
(let loop ((remaining editors)
(names '())
(result '()))
(cond
((null? remaining)
(reverse result))
((not (car remaining))
(loop (cdr remaining) names result))
(else
(let* ((editor (car remaining))
(name (car editor)))
(if (member name names)
(loop (cdr remaining) names result)
(loop (cdr remaining)
(cons name names)
(cons editor result))))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Return known GUI editors found on Windows.
; pre : The current platform is Windows.
; post : PATH and standard Windows installation locations were inspected.
; result : A list of editor descriptions.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (find-windows-editors)
(unique-editors
(list
(editor-on-path "vscode" "Visual Studio Code" "code.cmd" " --wait")
(editor-on-path "vscode" "Visual Studio Code" "code.exe" " --wait")
(editor-at
"vscode"
"Visual Studio Code"
(environment-path "LOCALAPPDATA" "Programs" "Microsoft VS Code" "bin" "code.cmd")
" --wait")
(editor-at
"vscode"
"Visual Studio Code"
(environment-path "ProgramFiles" "Microsoft VS Code" "bin" "code.cmd")
" --wait")
(editor-at
"vscode"
"Visual Studio Code"
(environment-path "ProgramFiles(x86)" "Microsoft VS Code" "bin" "code.cmd")
" --wait")
(editor-on-path "notepad++"
"Notepad++"
"notepad++.exe"
" -multiInst -nosession")
(editor-at
"notepad++"
"Notepad++"
(environment-path "ProgramFiles" "Notepad++" "notepad++.exe")
" -multiInst -nosession")
(editor-at
"notepad++"
"Notepad++"
(environment-path "ProgramFiles(x86)" "Notepad++" "notepad++.exe")
" -multiInst -nosession")
(editor-on-path "notepad" "Notepad" "notepad.exe" "")
(editor-at
"notepad"
"Notepad"
(environment-path "SystemRoot" "System32" "notepad.exe")
""))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Return known GUI editors found on macOS.
; pre : The current platform is macOS.
; post : PATH and standard application locations were inspected.
; result : A list of editor descriptions.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (find-macos-editors)
(unique-editors
(list
(editor-on-path "vscode" "Visual Studio Code" "code" " --wait")
(editor-at
"vscode"
"Visual Studio Code"
(string->path
"/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code")
" --wait")
(let ((open
(or (find-executable-path "open")
(let ((p (string->path "/usr/bin/open")))
(if (file-exists? p) p #f)))))
(if open
(list "textedit"
"TextEdit"
(format "~a -W -a TextEdit" (quote-command-path open)))
#f)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Return known GUI editors found on Unix/Linux.
; pre : The current platform is Unix.
; post : PATH and common Linux installation locations were inspected.
; result : A list of editor descriptions.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (find-unix-editors)
(unique-editors
(list
(editor-on-path "vscode" "Visual Studio Code" "code" " --wait")
(editor-on-path "kate" "Kate" "kate" " --block")
(editor-on-path "gedit" "Gedit" "gedit" " --wait")
(editor-on-path "xed" "Xed" "xed" " --wait")
(editor-at "vscode" "Visual Studio Code" (string->path "/snap/bin/code") " --wait")
(editor-at "vscode" "Visual Studio Code" (string->path "/usr/local/bin/code") " --wait")
(editor-at "vscode" "Visual Studio Code" (string->path "/usr/bin/code") " --wait")
(editor-at "kate" "Kate" (string->path "/usr/local/bin/kate") " --block")
(editor-at "kate" "Kate" (string->path "/usr/bin/kate") " --block")
(editor-at "gedit" "Gedit" (string->path "/usr/bin/gedit") " --wait")
(editor-at "xed" "Xed" (string->path "/usr/bin/xed") " --wait"))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Set the editor environment variables for Git commands started by git-cli.
; pre : command is a valid Git editor command.
; post : GIT_EDITOR and GIT_SEQUENCE_EDITOR contain command.
; result : command.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (set-editor-environment! command)
(putenv "GIT_EDITOR" command)
(putenv "GIT_SEQUENCE_EDITOR" command)
command)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Provided functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Return all well-known GUI editors found on the current platform.
; pre : The platform and filesystem are available.
; post : No editor has been started.
; result : A list of (name description command) items.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (find-editors)
(case (system-type 'os)
((windows) (find-windows-editors))
((macosx) (find-macos-editors))
((unix) (find-unix-editors))
(else '())))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Return official download pointers for optional GUI editors.
; pre : The current platform is known.
; post : No network request has been made.
; result : A list of (name description url) items.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (editor-downloads)
(case (system-type 'os)
((windows)
'(("vscode" "Visual Studio Code" "https://code.visualstudio.com/download")
("notepad++" "Notepad++" "https://notepad-plus-plus.org/downloads/")))
((macosx)
'(("vscode" "Visual Studio Code" "https://code.visualstudio.com/download")))
((unix)
'(("vscode" "Visual Studio Code" "https://code.visualstudio.com/download")))
(else '())))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Read the editor explicitly configured for git-cli.
; pre : The git-cli configuration is readable.
; post : The configuration has not been changed.
; result : The configured editor command, or #f.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (configured-editor)
(let ((editor (cfg-get 'git 'editor #f)))
(if (and (string? editor)
(not (string=? (string-trim editor) "")))
editor
#f)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Store and activate the editor command used by git-cli.
; pre : command is a command string suitable for GIT_EDITOR.
; post : The command has been stored and applied to the Git editor environment.
; result : command.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (set-editor! command)
(cfg-set! 'git 'editor command)
(set-editor-environment! command))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Return git-cli to automatic editor detection and activate that editor.
; pre : A well-known GUI editor can be found on the current platform.
; post : The explicit editor setting is cleared and the detected editor is active.
; result : The automatically detected editor command.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (set-editor-auto!)
(let ((editors (find-editors)))
(if (null? editors)
(error 'git-config "No well-known GUI editor found")
(begin
(cfg-set! 'git 'editor "")
(set-editor-environment! (caddr (car editors)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; goal : Find the GUI editor that git-cli should offer to Git.
; pre : The platform and git-cli configuration are available.
; post : No editor has been started.
; result : A configured or well-known GUI editor command, or #f.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (find-editor)
(or (configured-editor)
(let ((editors (find-editors)))
(if (null? editors)
#f
(caddr (car editors))))))