Skip to content

Commit 780abda

Browse files
committed
Support saving backup and autosave files in configurable directories
This commit is "minimal changes that achieve the new functionality".
1 parent cefb768 commit 780abda

File tree

3 files changed

+165
-31
lines changed

3 files changed

+165
-31
lines changed

gui-lib/framework/main.rkt

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -539,13 +539,73 @@
539539
path-utils:generate-autosave-name
540540
(-> (or/c #f path-string? path-for-some-system?) path?)
541541
(filename)
542-
@{Generates a name for an autosave file from @racket[filename].})
542+
@{
543+
Generates a path for an autosave file based on @racket[filename].
544+
545+
@index{'path-utils:autosave-dir}
546+
The value of @racket[(preferences:get 'path-utils:autosave-dir)]
547+
determines the directory of the resulting path.
548+
When the preference value is @racket[#f] (the default),
549+
result will use the same directory as the @racket[filename]
550+
(or, when @racket[filename] is @racket[#f], the directory determined by
551+
@racket[(find-system-path 'doc-dir)]).
552+
Otherwise, when @racket[(preferences:get 'path-utils:autosave-dir)]
553+
returns a path satisfying @racket[complete-path?] and
554+
@racket[directory-exists?], the autosave file will be saved in
555+
that directory.
556+
A relative @racket[filename] will be resolved based on
557+
the value of @racket[(current-directory)].
558+
559+
When @racket[filename] is @racket[#f], the final element of the
560+
resulting path will be an automatically-generated unique name.
561+
Otherwise, the final path element will be derived from @racket[filename].
562+
When @racket[(preferences:get 'path-utils:autosave-dir)] returns
563+
@racket[#f], the original file name will be used directly as the base;
564+
otherwise, base will be the complete path to @racket[filename],
565+
encoded by replacing each seperator (@litchar{\} on Windows or
566+
@litchar{/} on Unix or Mac OS) with @litchar{!}.
567+
This base is transformed into the final path element in a
568+
platform-specific manner:
569+
@itemlist[
570+
@item{On Unix and Mac OS, a @litchar{#} is added to the start
571+
and end of the file’s name, then a number is added after the
572+
ending @litchar{#}, and then one more @litchar{#} is appended
573+
after the number.
574+
The number is selected to make the autosave filename unique.}
575+
@item{On Windows, the file’s extension is replaced with a number
576+
to make the autosave filename unique.}
577+
]})
543578

544579
(proc-doc/names
545580
path-utils:generate-backup-name
546581
(path? . -> . path?)
547582
(filename)
548-
@{Generates a name for an backup file from @racket[filename].})
583+
@{
584+
Generates a path for a backup file based on @racket[filename].
585+
586+
@index{'path-utils:backup-dir}
587+
The value of @racket[(preferences:get 'path-utils:backup-dir)]
588+
determines the directory of the resulting path in much the same
589+
way as @racket[path-utils:generate-autosave-name] responds to
590+
the preference @racket['path-utils:autosave-dir]:
591+
when the value is @racket[#f] (the default), the directory of
592+
@racket[filename] is used, and otherwise the directory from the
593+
preference is used.
594+
595+
The final element of the resulting path is generated from
596+
@racket[filename] in a platform-specific manner:
597+
@itemlist[
598+
@item{On Unix and Mac OS, a @litchar{~} is added to the end
599+
of the file’s name.}
600+
@item{On Windows, the file’s extension is replaced
601+
with @litchar{.bak}.}]
602+
In either case, when @racket[(preferences:get 'path-utils:backup-dir)]
603+
returns a non-false value, the result of the above transformation
604+
is combined with the complete path of @racket[filename],
605+
encoded by replacing seperators with @litchar{!} as with
606+
@racket[path-utils:generate-autosave-name], to form the final
607+
path element.
608+
})
549609

550610
(parameter-doc
551611
finder:dialog-parent-parameter

gui-lib/framework/private/main.rkt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,31 @@
599599
(update-style-list
600600
(color-prefs:lookup-in-color-scheme 'framework:misspelled-text-color)))
601601

602+
;; for path-utils
603+
604+
(define (valid-maybe-path-value? v)
605+
(or (not v)
606+
(and (path? v)
607+
(complete-path? v)
608+
(directory-exists? v))))
609+
610+
(define (marshall:maybe-path->bytes v)
611+
(and (path? v) (path->bytes v)))
612+
613+
(define (unmarshall:maybe-bytes->path v)
614+
(with-handlers ([exn:fail? (λ (e) #f)])
615+
(and v (bytes->path v))))
616+
617+
(define (initialize-backup/autosave-preference sym)
618+
(preferences:set-default sym #f valid-maybe-path-value?)
619+
(preferences:set-un/marshall sym
620+
marshall:maybe-path->bytes
621+
unmarshall:maybe-bytes->path))
622+
623+
(initialize-backup/autosave-preference 'path-utils:backup-dir)
624+
625+
(initialize-backup/autosave-preference 'path-utils:autosave-dir)
626+
602627
;; groups
603628

604629
(preferences:set-default 'framework:exit-when-no-frames #t boolean?)
Lines changed: 78 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,49 @@
11
#lang scheme/unit
2-
(require "sig.rkt")
2+
(require "sig.rkt"
3+
"../preferences.rkt")
34

45
(import)
56
(export framework:path-utils^)
6-
7-
(define (generate-autosave-name name)
8-
(let-values ([(base name dir?)
9-
(if name
10-
(split-path name)
11-
(values (find-system-path 'doc-dir)
12-
(bytes->path-element #"mredauto")
13-
#f))])
14-
(let* ([base (if (path? base)
15-
base
16-
(current-directory))]
17-
[path (if (relative-path? base)
18-
(build-path (current-directory) base)
19-
base)])
20-
(let loop ([n 1])
7+
8+
;; preferences initialized in main.rkt
9+
10+
(define current-backup-dir
11+
(preferences:get/set 'path-utils:backup-dir))
12+
13+
(define current-autosave-dir
14+
(preferences:get/set 'path-utils:autosave-dir))
15+
16+
; generate-autosave-name : (or/c #f path-string? path-for-some-system?) -> path?
17+
(define (generate-autosave-name maybe-old-path)
18+
(cond
19+
[maybe-old-path
20+
(let*-values ([(base name dir?) (split-path maybe-old-path)]
21+
[(base) (cond
22+
[(not (path? base))
23+
(current-directory)]
24+
[(relative-path? base)
25+
(build-path (current-directory) base)]
26+
[else
27+
base])])
28+
(cond
29+
[(current-autosave-dir)
30+
=>
31+
(λ (dir)
32+
(make-unique-autosave-name dir (encode-as-path-element base name)))]
33+
[else
34+
(make-unique-autosave-name base name)]))]
35+
[else
36+
(make-unique-autosave-name (or (current-autosave-dir)
37+
(find-system-path 'doc-dir))
38+
(bytes->path-element #"mredauto"))]))
39+
40+
41+
; make-unique-autosave-name : dir-path path-element -> path?
42+
(define (make-unique-autosave-name dir name)
43+
(let loop ([n 1])
2144
(let* ([numb (string->bytes/utf-8 (number->string n))]
2245
[new-name
23-
(build-path path
46+
(build-path dir
2447
(if (eq? (system-type) 'windows)
2548
(bytes->path-element
2649
(bytes-append (regexp-replace #rx#"\\..*$"
@@ -36,22 +59,48 @@
3659
#"#"))))])
3760
(if (file-exists? new-name)
3861
(loop (add1 n))
39-
new-name))))))
62+
new-name))))
4063

4164
(define (generate-backup-name full-name)
4265
(let-values ([(pre-base name dir?) (split-path full-name)])
4366
(let ([base (if (path? pre-base)
4467
pre-base
4568
(current-directory))])
46-
(let ([name-bytes (path-element->bytes name)])
47-
(cond
48-
[(and (eq? (system-type) 'windows)
49-
(regexp-match #rx#"(.*)\\.[^.]*" name-bytes))
50-
=>
51-
(λ (m)
52-
(build-path base (bytes->path-element (bytes-append (cadr m) #".bak"))))]
53-
[(eq? (system-type) 'windows)
54-
(build-path base (bytes->path-element (bytes-append name-bytes #".bak")))]
55-
[else
56-
(build-path base (bytes->path-element (bytes-append name-bytes #"~")))])))))
69+
(define name-element
70+
(let ([name-bytes (path-element->bytes name)])
71+
(bytes->path-element
72+
(cond
73+
[(and (eq? (system-type) 'windows)
74+
(regexp-match #rx#"(.*)\\.[^.]*" name-bytes))
75+
=>
76+
(λ (m)
77+
(bytes-append (cadr m) #".bak"))]
78+
[(eq? (system-type) 'windows)
79+
(bytes-append name-bytes #".bak")]
80+
[else
81+
(bytes-append name-bytes #"~")]))))
82+
(cond
83+
[(current-backup-dir)
84+
=>
85+
(λ (dir)
86+
(build-path dir (encode-as-path-element base name-element)))]
87+
[else
88+
(build-path base name-element)]))))
89+
90+
91+
92+
; encode-as-path-element : dir-path path-element -> path-element
93+
; N.B. generate-backup-name may supply a relative directory, but
94+
; we should always use a complete one.
95+
; Using simplify-path does that and ensures no 'up or 'same
96+
; Using ! is not completely robust, but works well enough for Emacs.
97+
(define (encode-as-path-element base-maybe-relative name)
98+
(bytes->path-element
99+
(regexp-replace* (case (system-path-convention-type)
100+
[(windows) #rx#"\\\\"]
101+
[else #rx#"/"])
102+
(path->bytes
103+
(simplify-path (build-path base-maybe-relative name)))
104+
#"!")))
105+
57106

0 commit comments

Comments
 (0)