-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathutils.lisp
262 lines (222 loc) · 8.77 KB
/
utils.lisp
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
(in-package :able)
;;; Sequence and string utils
(defun split (string char)
"Split string on char."
(let ((string (string-right-trim " " string)))
(loop for i = 0 then (1+ j)
as j = (position char string :start i)
collect (subseq string i j)
while j)))
(defun string-replace (string from to)
(map 'string (lambda (char)
(if (eq char from)
to
char))
string))
(defun split-at (collection n)
"Split a sequence at position 'n', returning both halves."
(when (< n (length collection))
(values (subseq collection 0 n)
(subseq collection n))))
;(defun cl-user::sload (name)
; (asdf:oos 'asdf:load-op name))
(defun take (list n)
(let ((len (length list)))
(butlast list (max (min len (- len n)) 0))))
(defun foldl (fun id list)
(reduce (lambda (x y) (funcall fun x y)) list :initial-value id))
(defun randoms (count &optional (limit 64))
"Generate a list of size 'count' random numbers bounded by 'limit'"
(loop repeat count collect (random limit)))
(defun prefix-p (sequence prefix &optional (start 0))
"Is 'prefix' the prefix of 'sequence', optionally starting at 'start'."
(let ((seq-len (length sequence))
(pre-len (length prefix)))
(if (> (+ start pre-len) seq-len)
nil
(equalp (subseq sequence start (+ start pre-len)) prefix))))
;;;;;;;;;;;;;; file and directry handling ;;;;;;;;;;;;;;
(defun deduce-path-separator (pathstring)
"Is this a forwardslash or backslash platform?"
(if (find #\/ pathstring) #\/ #\\))
(defun dirname-from-pathstring (pathstring)
(subseq pathstring 0
(position
(deduce-path-separator pathstring)
pathstring :from-end t)))
(defun dirname-from-pathname (pathname)
(dirname-from-pathstring (namestring pathname)))
(defun filename-from-pathname (pathname)
(pathname-name (parse-namestring pathname)))
(defun filetype-from-pathstring (pathstring)
(let ((start (position #\. pathstring :from-end t)))
(when start
(subseq pathstring (1+ start)))))
(defun filename-from-pathstring (pathstring)
(let* ((separator (deduce-path-separator pathstring))
(start (position separator pathstring :from-end t)))
(when start
(let ((end (position #\. pathstring :from-end t)))
(if (and end (< end (length pathstring)) (> end start))
(subseq pathstring (1+ start) end)
(subseq pathstring (1+ start)))))))
(defun filetype? (path types)
"Is the file specified in path of one of the types specified in types?"
(member (filetype-from-pathstring path) types :test #'string=))
(defun lisp-file? (path)
"Does path represent a lisp source file?"
(filetype? path '("lisp" "cl" "l" "lsp")))
(defgeneric correct-path (pathname)
(:documentation "Normalises all paths such that backslashes become forward slashes"))
(defmethod correct-path ((pathname pathname))
(correct-path (namestring pathname)))
(defmethod correct-path ((pathname string))
(string-replace pathname #\\ #\/))
;;;;;;;;;;;;;; functions for dealing with Tk text indices ;;;;;;;;;;;;;;
(defun get-row-integer (text-index)
"Extracts the row number from a Tk text index."
(let ((temp nil))
(setf temp (first (split text-index #\.)))
(setf temp (read-from-string temp))))
(defun get-col-integer (text-index)
"Extracts the col number from a Tk text index."
(let ((temp nil))
(setf temp (second (split text-index #\.)))
(setf temp (read-from-string temp))))
(defun text-row-add (text-index increment)
"Increments the row of a Tk text index by increment (i.e. 5.0 -> 6.0)."
(let ((temp (get-row-integer text-index)))
(incf temp increment)
(if (> temp 0)
(format nil "~a.0" temp)
"1.0")))
(defun text-col-add (text-index increment)
"Increments the col of a Tk text index by increment (i.e. 4.1 -> 4.2)."
(let ((row (get-row-integer text-index))
(col (get-col-integer text-index)))
(incf col increment)
(format nil "~a.~a" row col)))
(defun strpos-to-textidx (str pos)
"Convert a string position into a Tk text index."
(let ((row 1)
(col 0)
(col-inc))
(incf row (count #\Newline str :end pos))
(setf col-inc (position #\Newline str :end pos :from-end t))
(if col-inc
(setf col (- pos col-inc 1))
(setf col pos))
(values (format nil "~a.~a" row col) row col)))
;;;;;;;;;;;;;; functions for dealing with strings ;;;;;;;;;;;;;;
(defun find-next-open (code-string start)
(let ((x 0) (pos start))
(loop while (> pos 0)
while (>= x 0) do
(decf pos)
(case (char code-string pos)
((#\() (decf x))
((#\)) (incf x))))
(if (< x 0) pos nil)))
(defun find-next-close (code-string start)
(let ((x 0) (pos start))
(labels ((find-next-close-inner (code-string)
(when (< pos (length code-string))
(case (char code-string pos)
((#\)) (decf x))
((#\() (incf x)))
(when (>= x 0)
(incf pos)
(find-next-close-inner code-string)))))
(find-next-close-inner code-string)
(if (< x 0) pos nil))))
(defun find-current-sexp (code-string pos)
(let ((start pos) (end pos))
(labels ((get-inner-form ()
(let* ((open (find-next-open code-string start))
(close (find-next-close code-string end)))
(when (and open close)
(setf start open)
(setf end (+ 1 close))
(when (and (> start 0) (< end (length code-string)))
(get-inner-form))))))
(get-inner-form)
(values (subseq code-string start end) start end))))
(defun trim-code (codestring)
"Tidy strings front and back."
(string-trim '(#\Newline #\Linefeed #\Space #\Tab #\Return) codestring))
(defun find-current-function (code-string pos)
"From the cursor, finds the current function as token, start and end indices."
(let ((next-open (find-next-open code-string pos))
start end token)
(when next-open
(setf start (get-col-integer (strpos-to-textidx code-string next-open)))
(loop :for i :from (1+ next-open) :below (length code-string)
:do (if (member (char code-string i) '(#\Space #\Tab #\Newline) :test #'char=)
(setf next-open i)
(return)))
(setf end (position #\Space code-string :start (1+ next-open) :test #'equal))
(setf token (subseq code-string (+ next-open 1) end)))
(values token start end)))
(defun longest-prefix-match (list)
"Takes a list of strings and returns their longest common lexical prefix."
(let ((best (if list (car list) ""))
(longest most-positive-fixnum))
(loop for this in (rest list) do
(let ((len (mismatch this best)))
(when (< len longest)
(setf longest len)
(setf best (subseq this 0 len)))))
best))
;;;;;;;;;;;;;; environment ;;;;;;;;;;;;;;
(defun shutdown ()
#+:clisp (ext:quit)
#+:sbcl (sb-ext:quit)
#+:ccl (ccl:quit))
(defun deliver ()
;;; SBCL seems to get upset if ABLE is running when this is called.
;;; It's best to load ABLE but not call start-able before invoking
;;; this on SBCL. Problem only manifests on Linux??
#+:sbcl (sb-ext:save-lisp-and-die "able"
:toplevel 'able:start
:executable t)
#+:clisp (ext:saveinitmem "able"
:init-function 'able:start
:executable t
:quiet t
:norc t)
#+:ccl (ccl:save-application "able"
:toplevel-function 'able:start
:prepend-kernel t
:error-handler :quit-quietly))
(defun function-lambda-list (fn)
"Return an argument list for the supplied function."
(let ((arglist))
(handler-case
#+:clisp (setf arglist (sys::arglist fn))
#+:sbcl (setf arglist (sb-introspect:function-lambda-list fn))
#+:ccl (setf arglist (ccl:arglist fn))
(error (ex) (declare (ignore ex)) (setf arglist nil)))
arglist))
(defun start-process (command-line)
(let ((process))
(progn
#+:clisp
(setf process
(ext:run-program (car command-line) :arguments (cdr command-line)
:input :stream :output :stream :wait t))
#+:sbcl
(let ((p (sb-ext:run-program (car command-line) (cdr command-line)
:input :stream :output :stream :error :output :wait nil :search t)))
(setf process
(make-two-way-stream
(sb-ext:process-output p)
(sb-ext:process-input p))))
#+:ccl
(let ((p (ccl:run-program (car command-line) (cdr command-line)
:input :stream :output t)))
(setf process
(make-two-way-stream
(ccl:external-process-output-stream p)
(ccl:external-process-input-stream p))))
(sleep 1))
process))