Editor configuration added.
This commit is contained in:
+159
-95
@@ -1,11 +1,14 @@
|
||||
#lang racket/base
|
||||
|
||||
(require racket/path
|
||||
racket/string
|
||||
"config.rkt")
|
||||
|
||||
(provide find-editor
|
||||
find-editors
|
||||
configured-editor
|
||||
set-editor!)
|
||||
set-editor!
|
||||
set-editor-auto!)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Supporting functions
|
||||
@@ -20,21 +23,6 @@
|
||||
(define (quote-command-path p)
|
||||
(format "\"~a\"" (path->string p)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Return an existing executable from a list of candidate paths.
|
||||
; pre : candidates contains paths or #f values.
|
||||
; post : The filesystem has only been inspected.
|
||||
; result : The first existing path, or #f.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (first-existing candidates)
|
||||
(cond
|
||||
((null? candidates) #f)
|
||||
((and (car candidates)
|
||||
(file-exists? (car candidates)))
|
||||
(car candidates))
|
||||
(else
|
||||
(first-existing (cdr candidates)))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Build a path below an environment variable when it is defined.
|
||||
; pre : variable is an environment variable name.
|
||||
@@ -48,102 +36,160 @@
|
||||
#f)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Find an editor executable on PATH and append its wait arguments.
|
||||
; 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 : An editor command string, or #f.
|
||||
; result : (name description command), or #f.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (editor-on-path executable arguments)
|
||||
(define (editor-on-path name description executable arguments)
|
||||
(let ((p (find-executable-path executable)))
|
||||
(if p
|
||||
(string-append (quote-command-path p) arguments)
|
||||
#f)))
|
||||
(editor-at name description p arguments)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Make an editor command from an existing well-known path.
|
||||
; pre : p is a path or #f; arguments contains the editor wait arguments.
|
||||
; post : p has only been inspected.
|
||||
; result : An editor command string, or #f.
|
||||
; 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 (editor-at p arguments)
|
||||
(if (and p (file-exists? p))
|
||||
(string-append (quote-command-path p) arguments)
|
||||
#f))
|
||||
(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 : Find a well-known GUI editor on Windows.
|
||||
; 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 Git editor command string, or #f.
|
||||
; result : A list of editor descriptions.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (find-windows-editor)
|
||||
(or (editor-on-path "code.cmd" " --wait")
|
||||
(editor-on-path "code.exe" " --wait")
|
||||
(editor-at
|
||||
(first-existing
|
||||
(list
|
||||
(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")))
|
||||
" --wait")
|
||||
(editor-at
|
||||
(first-existing
|
||||
(list
|
||||
(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")))
|
||||
" --wait")
|
||||
(editor-on-path "notepad.exe" "")
|
||||
(editor-at
|
||||
(environment-path "SystemRoot" "System32" "notepad.exe")
|
||||
"")
|
||||
#f))
|
||||
(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" "")
|
||||
(editor-at
|
||||
"notepad"
|
||||
"Notepad"
|
||||
(environment-path "SystemRoot" "System32" "notepad.exe")
|
||||
""))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Find a well-known GUI editor on macOS.
|
||||
; goal : Return known GUI editors found on macOS.
|
||||
; pre : The current platform is macOS.
|
||||
; post : PATH and standard application locations were inspected.
|
||||
; result : A Git editor command string, or #f.
|
||||
; result : A list of editor descriptions.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (find-macos-editor)
|
||||
(or (editor-on-path "code" " --wait")
|
||||
(editor-at
|
||||
(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
|
||||
(format "~a -W -a TextEdit" (quote-command-path open))
|
||||
#f))
|
||||
#f))
|
||||
(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 : Find a well-known GUI editor on Unix/Linux.
|
||||
; 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 Git editor command string, or #f.
|
||||
; result : A list of editor descriptions.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (find-unix-editor)
|
||||
(or (editor-on-path "code" " --wait")
|
||||
(editor-on-path "kate" " --block")
|
||||
(editor-on-path "gedit" " --wait")
|
||||
(editor-on-path "xed" " --wait")
|
||||
(editor-at (string->path "/snap/bin/code") " --wait")
|
||||
(editor-at (string->path "/usr/local/bin/code") " --wait")
|
||||
(editor-at (string->path "/usr/bin/code") " --wait")
|
||||
(editor-at (string->path "/usr/local/bin/kate") " --block")
|
||||
(editor-at (string->path "/usr/bin/kate") " --block")
|
||||
(editor-at (string->path "/usr/bin/gedit") " --wait")
|
||||
(editor-at (string->path "/usr/bin/xed") " --wait")
|
||||
#f))
|
||||
(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 : Read the editor explicitly configured for git-cli.
|
||||
; pre : The git-cli configuration is readable.
|
||||
@@ -151,16 +197,35 @@
|
||||
; result : The configured editor command, or #f.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (configured-editor)
|
||||
(cfg-get 'git 'editor #f))
|
||||
(let ((editor (cfg-get 'git 'editor #f)))
|
||||
(if (and (string? editor)
|
||||
(not (string=? (string-trim editor) "")))
|
||||
editor
|
||||
#f)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Store the editor command used by git-cli.
|
||||
; 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 in the git-cli configuration.
|
||||
; result : The result returned by the configuration layer.
|
||||
; post : The command has been stored and applied to the Git editor environment.
|
||||
; result : command.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (set-editor! command)
|
||||
(cfg-set! 'git '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.
|
||||
@@ -170,8 +235,7 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (find-editor)
|
||||
(or (configured-editor)
|
||||
(case (system-type 'os)
|
||||
((windows) (find-windows-editor))
|
||||
((macosx) (find-macos-editor))
|
||||
((unix) (find-unix-editor))
|
||||
(else #f))))
|
||||
(let ((editors (find-editors)))
|
||||
(if (null? editors)
|
||||
#f
|
||||
(caddr (car editors))))))
|
||||
|
||||
+61
-67
@@ -6,7 +6,6 @@
|
||||
racket/system
|
||||
"config.rkt"
|
||||
"find-editor.rkt"
|
||||
"find-mergetool.rkt"
|
||||
)
|
||||
|
||||
(provide git-exe
|
||||
@@ -96,75 +95,70 @@
|
||||
; read completely.
|
||||
; result : The exit code and ordered (source line) output items.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (run-git args #:input (input #f))
|
||||
(let* ((env (environment-variables-copy
|
||||
(current-environment-variables)))
|
||||
(editor (find-editor))
|
||||
(mergetool-path (find-mergetool-path)))
|
||||
(environment-variables-set! env
|
||||
#"GIT_TERMINAL_PROMPT"
|
||||
#"0")
|
||||
; goal : Configure the process environment used by git-cli Git commands.
|
||||
; pre : The editor finder can inspect the current platform.
|
||||
; post : Terminal prompting is disabled and a detected GUI editor is made
|
||||
; available to Git and Git's sequence editor.
|
||||
; result : void.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (setup-git-environment!)
|
||||
(putenv "GIT_TERMINAL_PROMPT" "0")
|
||||
(let ((editor (find-editor)))
|
||||
(when editor
|
||||
(let ((editor-bytes (string->bytes/utf-8 editor)))
|
||||
(environment-variables-set! env
|
||||
#"GIT_EDITOR"
|
||||
editor-bytes)
|
||||
(environment-variables-set! env
|
||||
#"GIT_SEQUENCE_EDITOR"
|
||||
editor-bytes)))
|
||||
(when mergetool-path
|
||||
(let* ((directory (path-only mergetool-path))
|
||||
(old-path (environment-variables-ref env #"PATH"))
|
||||
(separator (if (eq? (system-type 'os) 'windows) ";" ":"))
|
||||
(new-path
|
||||
(if old-path
|
||||
(string-append (path->string directory)
|
||||
separator
|
||||
(bytes->string/utf-8 old-path))
|
||||
(path->string directory))))
|
||||
(environment-variables-set! env
|
||||
#"PATH"
|
||||
(string->bytes/utf-8 new-path))))
|
||||
(parameterize ((current-environment-variables env))
|
||||
(let-values (((process stdout stdin stderr)
|
||||
(apply subprocess
|
||||
#f
|
||||
#f
|
||||
#f
|
||||
(git-exe)
|
||||
(map (λ (arg) (format "~a" arg)) args)
|
||||
)))
|
||||
(when input
|
||||
(display input stdin)
|
||||
(flush-output stdin))
|
||||
(close-output-port stdin)
|
||||
(let ((output-channel (make-channel)))
|
||||
(define (read-output source port)
|
||||
(thread
|
||||
(λ ()
|
||||
(let loop ()
|
||||
(let ((line (read-line port)))
|
||||
(channel-put output-channel (list source line))
|
||||
(if (eof-object? line)
|
||||
(close-input-port port)
|
||||
(loop)))))))
|
||||
(putenv "GIT_EDITOR" editor)
|
||||
(putenv "GIT_SEQUENCE_EDITOR" editor)))
|
||||
(void))
|
||||
|
||||
(read-output 'stdout stdout)
|
||||
(read-output 'stderr stderr)
|
||||
(setup-git-environment!)
|
||||
|
||||
(let loop ((open-ports 2)
|
||||
(result '()))
|
||||
(if (= open-ports 0)
|
||||
(begin
|
||||
(subprocess-wait process)
|
||||
(values (subprocess-status process)
|
||||
(reverse result)))
|
||||
(let* ((output (channel-get output-channel))
|
||||
(line (cadr output)))
|
||||
(if (eof-object? line)
|
||||
(loop (- open-ports 1) result)
|
||||
(loop open-ports
|
||||
(cons output result)))))))))))
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; goal : Run Git without allowing interactive terminal prompts.
|
||||
; pre : args contains the Git command and its arguments; input is #f or a string
|
||||
; that must be written to Git's standard input.
|
||||
; post : Optional input has been written and standard output and error have been
|
||||
; read completely.
|
||||
; result : The exit code and ordered (source line) output items.
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(define (run-git args #:input (input #f))
|
||||
(let-values (((process stdout stdin stderr)
|
||||
(apply subprocess
|
||||
#f
|
||||
#f
|
||||
#f
|
||||
(git-exe)
|
||||
(map (λ (arg) (format "~a" arg)) args)
|
||||
)))
|
||||
(when input
|
||||
(display input stdin)
|
||||
(flush-output stdin))
|
||||
(close-output-port stdin)
|
||||
(let ((output-channel (make-channel)))
|
||||
(define (read-output source port)
|
||||
(thread
|
||||
(λ ()
|
||||
(let loop ()
|
||||
(let ((line (read-line port)))
|
||||
(channel-put output-channel (list source line))
|
||||
(if (eof-object? line)
|
||||
(close-input-port port)
|
||||
(loop)))))))
|
||||
|
||||
(read-output 'stdout stdout)
|
||||
(read-output 'stderr stderr)
|
||||
|
||||
(let loop ((open-ports 2)
|
||||
(result '()))
|
||||
(if (= open-ports 0)
|
||||
(begin
|
||||
(subprocess-wait process)
|
||||
(values (subprocess-status process)
|
||||
(reverse result)))
|
||||
(let* ((output (channel-get output-channel))
|
||||
(line (cadr output)))
|
||||
(if (eof-object? line)
|
||||
(loop (- open-ports 1) result)
|
||||
(loop open-ports
|
||||
(cons output result)))))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Provided utility functions
|
||||
|
||||
Reference in New Issue
Block a user