-
Notifications
You must be signed in to change notification settings - Fork 3
/
file-tree.scm
351 lines (294 loc) · 11.8 KB
/
file-tree.scm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
;; Dropping the builtin, in lieu of something that uses the global context?
(require (prefix-in helix. "helix/commands.scm"))
(require (prefix-in helix.static. "helix/static.scm"))
(require "helix/misc.scm")
(require "helix/editor.scm")
; (require "steel/sorting/merge-sort.scm")
;;; -----------------------------------------------------------------
;;; Merge two lists of numbers which are already in increasing order
(define merge-lists
(lambda (l1 l2 comparator)
(if (null? l1)
l2
(if (null? l2)
l1
(if (comparator (car l1) (car l2))
(cons (car l1) (merge-lists (cdr l1) l2 comparator))
(cons (car l2) (merge-lists (cdr l2) l1 comparator)))))))
;;; -------------------------------------------------------------------
;;; Given list l, output those tokens of l which are in even positions
(define even-numbers
(lambda (l)
(if (null? l) '() (if (null? (cdr l)) '() (cons (car (cdr l)) (even-numbers (cdr (cdr l))))))))
;;; -------------------------------------------------------------------
;;; Given list l, output those tokens of l which are in odd positions
(define odd-numbers
(lambda (l)
(if (null? l)
'()
(if (null? (cdr l)) (list (car l)) (cons (car l) (odd-numbers (cdr (cdr l))))))))
;;; ---------------------------------------------------------------------
;;; Use the procedures above to create a simple and efficient merge-sort
(define (merge-sort l #:comparator [comparator <])
(if (null? l)
l
(if (null? (cdr l))
l
(merge-lists (merge-sort (odd-numbers l) #:comparator comparator)
(merge-sort (even-numbers l) #:comparator comparator)
comparator))))
(provide fold-directory
unfold-all-one-level
open-file-from-picker
create-file
create-directory
fold-all
FILE-TREE
FILE-TREE-KEYBINDINGS
create-file-tree
file-tree-set-side!)
;; labelled buffers ->
(require (only-in "labelled-buffers.scm"
make-new-labelled-buffer!
temporarily-switch-focus
open-or-switch-focus
currently-in-labelled-buffer?
open-labelled-buffer
maybe-fetch-doc-id
fetch-doc-id))
;; TODO: This should be moved to a shared module somewhere, once the component API is cleaned up
(define (helix-prompt! prompt-str thunk)
(push-component! (prompt prompt-str thunk)))
;; TODO: Prefix function names to keep them separate
;; File Tree keybindings
(define FILE-TREE-KEYBINDINGS
(hash "normal"
(hash "i"
'no_op
"v"
'no_op
"|"
'no_op
"!"
'no_op
"A-!"
'no_op
"$"
'no_op
"C-a"
'no_op
"C-x"
'no_op
"a"
'no_op
"I"
'no_op
"o"
'no_op
"O"
'no_op
"d"
'no_op
"A-d"
'no_op
"F"
'no_op
"tab"
':fold-directory
"E"
':unfold-all-one-level
"o"
':open-file-from-picker
"n"
(hash "f" ':create-file "d" ':create-directory)
"F"
':fold-all)))
;; This needs to be globally unique
(define FILE-TREE "github.com/mattwparas/helix-config/file-tree")
(define file-tree-open-to-side 'left)
;; TODO: This should probably be a contract
(define (file-tree-set-side! side)
(unless (or (equal? side 'left) (equal? side 'right))
(error! "file-tree-set-side! requires either the 'left or 'right"))
(set! file-tree-open-to-side side))
(define *file-tree* '())
(define *directories* (hash))
(define *ignore-set* (hashset "target" ".git"))
(define (fold! directory)
(set! *directories* (hash-insert *directories* directory #t)))
(define (unfold! directory)
(set! *directories* (hash-insert *directories* directory #f)))
(define (flatten x)
(cond
[(null? x) '()]
[(not (list? x)) (list x)]
[else (append (flatten (car x)) (flatten (cdr x)))]))
(define (format-dir path)
(if (hash-contains? *directories* path)
(if (hash-try-get *directories* path) "> " "v ")
"> " ;; First time we're visiting, mark as closed
))
(define *extension-map* (hash "rs" " " "scm" " "))
(define (path->symbol path)
(let ([extension (path->extension path)])
(if (not (void? extension))
(begin
(define lookup (hash-try-get *extension-map* (path->extension path)))
(if lookup lookup " "))
" ")))
;; Simple tree implementation
;; Walks the file structure and prints without much fancy formatting
;; Returns a list of the visited files for convenience
(define (tree p writer-thunk)
(define (tree-rec path padding)
(define name (file-name path))
(if (hashset-contains? *ignore-set* name)
'()
(begin
(writer-thunk
(string-append padding (if (is-dir? path) (format-dir path) (path->symbol path)) name))
(cond
[(is-file? path) path]
[(is-dir? path)
;; If we're not supposed to see this path (i.e. its been folded),
;; then we're going to ignore it
;; Also - if it doesn't exist in the set, default it to folded
(if (not (hash-contains? *directories* path))
(begin
(set! *directories* (hash-insert *directories* path #t))
(list path))
(if (hash-try-get *directories* path)
(list path)
(cons path
(map (fn (x) (tree-rec x (string-append padding " ")))
(merge-sort (read-dir path) #:comparator string<?)))))]
[else void]))))
(flatten (tree-rec p "")))
;;@doc
;; Open the currently selected line
(define (open-file-from-picker)
(when (currently-in-labelled-buffer? FILE-TREE)
(define file-to-open (list-ref *file-tree* (helix.static.get-current-line-number)))
(helix.open file-to-open)))
;; Initialize all roots to be flat so that we don't blow things up, recursion only goes in to things
;; that are expanded
(define (create-file-tree)
;; The doc id, or #false if it is not in the map
(define doc-id (maybe-fetch-doc-id FILE-TREE))
(unless doc-id
(make-new-labelled-buffer! #:label FILE-TREE #:side file-tree-open-to-side))
(unless (editor-doc-exists? (fetch-doc-id FILE-TREE))
(make-new-labelled-buffer! #:label FILE-TREE #:side file-tree-open-to-side))
(temporarily-switch-focus
(lambda ()
(open-labelled-buffer FILE-TREE)
;; Open depending on the setting
(cond
[(equal? file-tree-open-to-side 'left) (helix.static.move-window-far-left)]
[(equal? file-tree-open-to-side 'right) (helix.static.move-window-far-right)]
[else void])
;;
(helix.static.move-window-far-left)
(helix.static.select_all)
(helix.static.delete_selection)
;; Update the current file tree value
(set! *file-tree*
(tree (helix-find-workspace)
(lambda (str)
(helix.static.insert_string str)
(helix.static.open_below)
(helix.static.goto_line_start)))))))
;;@doc
;; Fold the directory that we're currently hovering over
(define (fold-directory)
(when (currently-in-labelled-buffer? FILE-TREE)
(define directory-to-fold (list-ref *file-tree* (helix.static.get-current-line-number)))
(when (is-dir? directory-to-fold)
(begin
;; If its already folded, unfold it
(if (hash-try-get *directories* directory-to-fold)
(unfold! directory-to-fold)
(fold! directory-to-fold))
(update-file-tree)))))
;;@doc
;; Create a file under wherever we are
(define (create-file)
(when (currently-in-labelled-buffer? FILE-TREE)
(define currently-selected (list-ref *file-tree* (helix.static.get-current-line-number)))
(define prompt
(if (is-dir? currently-selected)
(string-append "New file: " currently-selected "/")
(string-append "New file: "
(trim-end-matches currently-selected (file-name currently-selected)))))
(helix-prompt!
prompt
(lambda (result)
(define file-name (string-append (trim-start-matches prompt "New file: ") result))
(temporarily-switch-focus (lambda ()
(helix.vsplit-new)
(helix.open file-name)
(helix.write file-name)
(helix.quit)))
;; TODO:
;; This is happening before the write is finished, so its not working. We will have to manually insert
;; the new file into the right spot in the tree, which would require rewriting this to have a proper sorted
;; tree representation in memory, which we don't yet have. For now, we can just do this I guess
(enqueue-thread-local-callback refresh-file-tree)))))
(define (update-file-tree)
(define current-selection (helix.static.current-selection-object))
; (define line-number (helix.static.get-current-line-number))
(define last-mode (editor-mode))
(helix.static.select_all)
(helix.static.delete_selection)
;; Update the current file tree value
(set! *file-tree*
(tree (helix-find-workspace)
(lambda (str)
(helix.static.insert_string str)
(helix.static.open_below)
(helix.static.goto_line_start))))
;; Set it BACK to where we were previously!
;; TODO: Currently the following bug exists:
;; Open helix, open file tree, run SPC-b to open the file tree
;; buffer (there should now be two of them). Press TAB, then F.
;; Helix will crash. One way to fix it is to not update the selection,
;; however that makes the file tree experience way worse. A better
;; way for now is to just disallow that command in the file tree
;; buffer since I haven't yet figured out how to get it working.
(helix.static.set-current-selection-object! current-selection)
(editor-set-mode! last-mode))
(define (refresh-file-tree)
(temporarily-switch-focus (lambda ()
(open-labelled-buffer FILE-TREE)
(update-file-tree))))
;;@doc
;; Create a new directory
(define (create-directory)
(when (currently-in-labelled-buffer? FILE-TREE)
(define currently-selected (list-ref *file-tree* (helix.static.get-current-line-number)))
(define prompt
(if (is-dir? currently-selected)
(string-append "New directory: " currently-selected "/")
(string-append "New directory: "
(trim-end-matches currently-selected (file-name currently-selected)))))
(helix-prompt! prompt
(lambda (result)
(define directory-name
(string-append (trim-start-matches prompt "New directory: ") result))
(hx.create-directory directory-name)
(enqueue-thread-local-callback refresh-file-tree)))))
;;@doc
;; Fold all of the directories
(define (fold-all)
(when (currently-in-labelled-buffer? FILE-TREE)
(set! *directories*
(transduce *directories* (mapping (lambda (x) (list (list-ref x 0) #t))) (into-hashmap)))
(helix.static.goto_file_start)
(refresh-file-tree)))
;;@doc
;; Unfold all of the currently open directories one level.
(define (unfold-all-one-level)
(when (currently-in-labelled-buffer? FILE-TREE)
(set! *directories*
(transduce *directories* (mapping (lambda (x) (list (list-ref x 0) #f))) (into-hashmap)))
(refresh-file-tree)))