forked from godotengine/emacs-gdscript-mode
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgdscript-utils.el
168 lines (140 loc) · 6.75 KB
/
gdscript-utils.el
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
;;; gdscript-utils.el --- Utility functions for gdscript-mode -*- lexical-binding: t; -*-
;; Copyright (C) 2020 GDQuest
;; Author: Nathan Lovato <[email protected]>, Fabián E. Gallina <[email protected]>
;; URL: https://github.com/godotengine/emacs-gdscript-mode/
;; Version: 0.1.0
;; Package-Requires: ((emacs "26.3"))
;; Maintainer: [email protected]
;; Created: Jan 2020
;; Keywords: languages
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; Misc utility functions for GDScript mode.
;;; Code:
(require 'gdscript-syntax)
(require 'gdscript-customization)
(defun gdscript--util-goto-line (line-number)
"Move point to LINE-NUMBER."
(goto-char (point-min))
(forward-line (1- line-number)))
(defun gdscript--util-forward-comment (&optional direction)
"Gdscript mode specific version of `forward-comment'.
Optional argument DIRECTION defines the direction to move to."
(let ((comment-start (gdscript-syntax-context 'comment))
(factor (if (< (or direction 0) 0)
-99999
99999)))
(when comment-start
(goto-char comment-start))
(forward-comment factor)))
(defun gdscript--util-list-directories (directory &optional predicate max-depth)
"List DIRECTORY subdirs, filtered by PREDICATE and limited by MAX-DEPTH.
Argument PREDICATE defaults to `identity' and must be a function
that takes one argument (a full path) and returns non-nil for
allowed files. When optional argument MAX-DEPTH is non-nil, stop
searching when depth is reached, else don't limit."
(let* ((dir (expand-file-name directory))
(dir-length (length dir))
(predicate (or predicate #'identity))
(to-scan (list dir))
(tally nil))
(while to-scan
(let ((current-dir (car to-scan)))
(when (funcall predicate current-dir)
(setq tally (cons current-dir tally)))
(setq to-scan (append (cdr to-scan)
(gdscript--util-list-files
current-dir #'file-directory-p)
nil))
(when (and max-depth
(<= max-depth
(length (split-string
(substring current-dir dir-length)
"/\\|\\\\" t))))
(setq to-scan nil))))
(nreverse tally)))
(defun gdscript--util-list-files (dir &optional predicate)
"List files in DIR, filtering with PREDICATE.
Argument PREDICATE defaults to `identity' and must be a function
that takes one argument (a full path) and returns non-nil for
allowed files."
(let ((dir-name (file-name-as-directory dir)))
(apply #'nconc
(mapcar (lambda (file-name)
(let ((full-file-name (expand-file-name file-name dir-name)))
(when (and
(not (member file-name '("." "..")))
(funcall (or predicate #'identity) full-file-name))
(list full-file-name))))
(directory-files dir-name)))))
(defun gdscript-util--find-project-configuration-file (&optional start-path)
"Return the path to the file \"project.godot\".
Start the search from START-PATH if provided. Otherwise, the search
starts from the current buffer path.
WARNING: the Godot project must exist for this function to work."
(let* ((base-path (or start-path default-directory))
(dominating-file
(locate-dominating-file base-path
(lambda (parent)
(directory-files parent t "project.godot")))))
(when dominating-file (expand-file-name dominating-file))))
(defun gdscript-util--get-godot-project-name ()
"Retrieve the project name from Godot's configuration file."
(with-temp-buffer
(insert-file-contents (concat (gdscript-util--find-project-configuration-file) "project.godot"))
(goto-char (point-min))
(if (re-search-forward "config/name=\"\\([^\"]*\\)\"" nil t)
(match-string 1)
(error "Could not find the name of the project"))))
(defun gdscript-util--get-godot-buffer-name (&optional editor)
"Return buffer name for godot's stdout/stderr output."
(format (if editor "*godot - %s - Editor*" "*godot - %s*") (gdscript-util--get-godot-project-name)))
(defun gdscript-util--get-gdformat-buffer-name ()
"Return buffer name for godot's stdout/stderr output."
(format "*gdformat - %s*" (gdscript-util--get-godot-project-name)))
(defun gdscript-util--get-godot-project-file-path-relative (file-path)
"Return the relative path of `FILE-PATH' to Godot's configuration file."
(let ((project-configuration-file (gdscript-util--find-project-configuration-file)))
(when project-configuration-file
(concat (file-name-sans-extension
(file-relative-name file-path project-configuration-file))))))
(defun gdscript-util--flatten (xs)
"Flatten deeply nested list.
For example:
> (gdscript-util--flatten (list 1 2 (list 3 (list 4 5)) nil))
> (1 2 3 4 5)
"
(cond
((null xs) nil)
((listp xs) (append (gdscript-util--flatten (car xs)) (gdscript-util--flatten (cdr xs))))
(t (list xs))))
(defun gdscript-util--read (items &optional prompt)
"Let's choose single item from ITEMS from mini-buffer.
PROMPT is prompt for read command. Return `nil' if user aborts."
(let* ((p (if prompt prompt "Options"))
(result (cond ((and (featurep 'projectile) )
(projectile-completing-read (format "%s: " p) items))
((fboundp 'ivy-read)
(ivy-read (format "%s: " p) items))
((fboundp 'ido-completing-read)
(ido-completing-read (format "%s: " p) items))
(t
(completing-read (format "%s (hit TAB to auto-complete): " p) items nil t)))))
(if quit-flag nil result)))
(defmacro gdscript-util--with-available-hydra (&rest body)
""
`(progn
(when (not (featurep 'hydra))
(error "No `hydra.el' available. To execute `gdscript-hydra-show' command you need to install hydra.el"))
,@body))
(provide 'gdscript-utils)
;;; gdscript-utils.el ends here