mirror of
https://github.com/godotengine/emacs-gdscript-mode.git
synced 2025-12-31 21:48:34 +03:00
Compare commits
78 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
099ca4471f | ||
|
|
828ff63da1 | ||
|
|
110d36da96 | ||
|
|
1a31e42273 | ||
|
|
13812f4a17 | ||
|
|
08654b4e38 | ||
|
|
01a0c56bb4 | ||
|
|
4db7ed6369 | ||
|
|
a1ff8e2801 | ||
|
|
949ac8c47a | ||
|
|
2e9f6fbe2a | ||
|
|
3f33cb6611 | ||
|
|
4ff18e6755 | ||
|
|
4a95d69034 | ||
|
|
16a4bc6c46 | ||
|
|
7adaf201c1 | ||
|
|
6744e3b74d | ||
|
|
7aea87bd7b | ||
|
|
123c0df70d | ||
|
|
4bf308023b | ||
|
|
e8f56823eb | ||
|
|
86dcaa2430 | ||
|
|
e31da43351 | ||
|
|
fd6754d36b | ||
|
|
c57e2a88f3 | ||
|
|
3fc2e92ce7 | ||
|
|
04732e7e46 | ||
|
|
ce95576aa8 | ||
|
|
fe17656b4e | ||
|
|
506480939f | ||
|
|
7eb2a3156d | ||
|
|
dbe43ebb0d | ||
|
|
c772b6e3af | ||
|
|
651622c860 | ||
|
|
cfeba7e72b | ||
|
|
35fe376cc3 | ||
|
|
4fcfc61c0e | ||
|
|
18c86dbb6c | ||
|
|
b768f80498 | ||
|
|
d55471dd02 | ||
|
|
8e61cf6faf | ||
|
|
3ad2680edf | ||
|
|
8d8b710ca9 | ||
|
|
f94b8d209c | ||
|
|
36c92dff15 | ||
|
|
87ba0803e0 | ||
|
|
3e46706855 | ||
|
|
ef3f0c529f | ||
|
|
194552fcb8 | ||
|
|
a52e91c3a6 | ||
|
|
e4981f7656 | ||
|
|
fea2afb96d | ||
|
|
b45fa69944 | ||
|
|
db2f50f9bf | ||
|
|
59753af564 | ||
|
|
46c17d352f | ||
|
|
f89d087f3a | ||
|
|
b6e1f9a512 | ||
|
|
a0027df0a1 | ||
|
|
6a1a894a14 | ||
|
|
4ddfc8edce | ||
|
|
0718ca9b09 | ||
|
|
c74e2cd77b | ||
|
|
584d61b6aa | ||
|
|
028df8c749 | ||
|
|
9a74dd3b6c | ||
|
|
6a3e4070bb | ||
|
|
753f4e6be6 | ||
|
|
94645632c5 | ||
|
|
b4d2bec304 | ||
|
|
7b45e32e8d | ||
|
|
2752ab4ad3 | ||
|
|
9f64525155 | ||
|
|
dc2100f9f4 | ||
|
|
68c425b57f | ||
|
|
2e0468b4b9 | ||
|
|
bdf31f54d5 | ||
|
|
e30e890d62 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -9,3 +9,6 @@
|
||||
|
||||
# Undo-tree save-files
|
||||
*.~undo-tree
|
||||
|
||||
#emacs projectile
|
||||
.projectile
|
||||
76
CHANGELOG.md
76
CHANGELOG.md
@@ -2,6 +2,82 @@
|
||||
|
||||
This document lists new features, improvements, changes, and bug fixes in each release of the package.
|
||||
|
||||
## GDScript mode 1.3.0
|
||||
|
||||
This release brings many quality-of-life improvements to work more productively with Godot and Emacs.
|
||||
|
||||
Big thanks to @VlachJosef and @rileyrg for contributing to this release.
|
||||
|
||||
### Features
|
||||
|
||||
- Support for running the project and scenes with [hydra](https://github.com/abo-abo/hydra) with `gdscript-hydra-show`.
|
||||
- Command interpreter (`comint` support) for Godot processes: get and navigate errors within Emacs, and jump to files and GDScript code causing errors.
|
||||
- The command buffer pops up automatically when you run a scene or the project in the editor.
|
||||
- Added command `gdscript-format-all` to format all gdscript buffers.
|
||||
- Added commands `gdscript-format-all` and `gdscript-format-buffer` to gdscript-mode's hydra window (`gdscript-hydra-show`).
|
||||
- Added command interpreter support for `gdscript-format-*` commands
|
||||
- Add the ability to open a local copy of the Godot docs with `gdscript-docs-*` commands.
|
||||
- Multiple projects support. Every project's `godot` process runs in its own buffer.
|
||||
- Godot's standard output and standard error are fed to a `comint` buffer. This allows you to navigate errors and jump to the corresponding source files, using `compilation-*` commands.
|
||||
- Hydra provides a history of commands for quick re-execution of godot commands. It also provides a quick way to rerun the last command.
|
||||
- The `gdscript-godot-run-current-scene` command now offers to run any scene file if the current buffer is not a scene file.
|
||||
- The `gdscript-godot-run-current-script` command now offers to run any script file if the current buffer is not a script file.
|
||||
- Syntax highlighting for the `$` operator.
|
||||
|
||||
### Improvements
|
||||
|
||||
- Added unit tests (see `gdscript-tests.el`).
|
||||
- Added check for missing `godot` executable and a corresponding error message.
|
||||
- You can force selecting a scene when calling `gdscript-godot-run-current-scene` by using the universal argument (<kbd>C-u</kbd>).
|
||||
- `gdscript-godot-run-current-scene` will use `projectile` by default if available, otherwise `ivy` or `ido`.
|
||||
- You can now customize the URL for the Godot API reference: `gdscript-docs-online-search-api-url`.
|
||||
- Also, use the universal argument (<kbd>C-u</kbd>) before calling `gdscript-docs-browse-api` to force it to use the online docs, even if a local build of the docs is available.
|
||||
|
||||
### Changes
|
||||
|
||||
- Removed guessing indentation size, which could guess indent sizes wrong.
|
||||
- Removed the customizable variables `gdscript-indent-guess-indent-offset` and `gdscript-indent-guess-indent-offset-verbose`.
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Fixed auto-indentation not working with match blocks.
|
||||
- Fixed auto-indentation of new blocks sometimes over-indenting.
|
||||
- Fixed `eww-after-render-hook` always calling gdscript docs formatter, even outside of `gdscript-mode` buffers.
|
||||
- Removed call to nonexisting function `f-executable-p`.
|
||||
|
||||
## GDScript mode 1.2.0
|
||||
|
||||
### Features
|
||||
|
||||
- Added commands to open the API reference in `eww`.
|
||||
- Added debug options when running `gdscript-godot-run-project-debug`.
|
||||
- Added a command to insert a path to a project file, either using `project-find-file` if `projectile` is available, otherwise with `find-file`.
|
||||
- Added a command to format a selected region with `gdformat`.
|
||||
- Added syntax highlighting for function calls.
|
||||
- Added missing built in functions.
|
||||
- Added missing `puppet` and `remotesync` keywords.
|
||||
|
||||
### Changes
|
||||
|
||||
- Changed keyboard shortcuts:
|
||||
- <kbd>C-c i</kbd> `gdscript-completion-insert-file-path-at-point`
|
||||
- <kbd>C-c C-f r</kbd> `gdscript-format-region`
|
||||
- <kbd>C-c C-f b</kbd> `gdscript-format-buffer`
|
||||
- <kbd>C-c C-r p</kbd> `gdscript-godot-open-project-in-editor`
|
||||
- <kbd>C-c C-r r</kbd> `gdscript-godot-run-project`
|
||||
- <kbd>C-c C-r d</kbd> `gdscript-godot-run-project-debug`
|
||||
- <kbd>C-c C-r s</kbd> `gdscript-godot-run-current-scene`
|
||||
- <kbd>C-c C-r q</kbd> `gdscript-godot-run-current-scene-debug`
|
||||
- <kbd>C-c C-r e</kbd> `gdscript-godot-edit-current-scene`
|
||||
- <kbd>C-c C-r x</kbd> `gdscript-godot-run-current-script`
|
||||
- <kbd>C-c C-b a</kbd> `gdscript-docs-browse-api`
|
||||
- <kbd>C-c C-b o</kbd> `gdscript-docs-browse-symbol-at-point`
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- Fixed loading the `gdscript-godot` module at initialization.
|
||||
- Fixed function calls in the mode map.
|
||||
|
||||
## GDScript mode 1.1.0
|
||||
|
||||
Emacs GDScript mode is now available on the [MELPA](https://melpa.org/) package archive!
|
||||
|
||||
88
README.md
88
README.md
@@ -22,6 +22,7 @@ This mode already features all the essentials:
|
||||
[gdformat](https://github.com/scony/godot-gdscript-toolkit/).
|
||||
- Auto-completion for all the keywords in the `gdscript-keywords.el` file.
|
||||
- Run or open the project and files with Godot.
|
||||
- Browsing the API reference in Emacs.
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -64,17 +65,19 @@ Then, in your init.el file, you can require the package:
|
||||
|
||||
### Installing in Spacemacs
|
||||
|
||||
1. Clone the repository to the `private/local` subdirectory of your `.emacs.d`
|
||||
directory, where you installed spacemacs.
|
||||
2. Add the package to the `dotspacemacs-additional-packages` and mark it as
|
||||
local. That's Spacemacs' feature to make it easy to load locally installed
|
||||
packages.
|
||||
1. Add the package to the `dotspacemacs-additional-packages`. You can find it under the dotspacemacs/layers function:
|
||||
|
||||
```lisp
|
||||
dotspacemacs-additional-packages '((gdscript-mode :location local))
|
||||
(defun dotspacemacs/layers ()
|
||||
"Configuration Layers declaration..."
|
||||
(setq-default
|
||||
;; ...
|
||||
dotspacemacs-additional-packages '(gdscript-mode)
|
||||
;; ...
|
||||
))
|
||||
```
|
||||
|
||||
3. In your `dotspacemacs/user-config` function, require the package.
|
||||
2. In your `dotspacemacs/user-config` function, require the package.
|
||||
|
||||
```lisp
|
||||
(defun dotspacemacs/user-config ()
|
||||
@@ -135,10 +138,36 @@ If you don't have `godot` available there, you can set a custom executable name
|
||||
|
||||
You can also use `customize` to change this path: `M-x customize` and search for "godot".
|
||||
|
||||
### Running Godot with visual debug options
|
||||
|
||||
When running `gdscript-godot-run-project-debug`, you can use the universal argument <kbd>C-u</kbd> to invoke a mini-buffer with extra options to pass to godot.
|
||||
|
||||
Here are the available options:
|
||||
|
||||
1. `<no options>` _(default)_
|
||||
2. `--debug-collisions`
|
||||
3. `--debug-navigation`
|
||||
4. `--debug-collisions --debug-navigation`
|
||||
|
||||
The last selected option is saved for the next time you call `gdscript-godot-run-project-debug`. To remove debug options, you need to call the command with the universal argument again.
|
||||
|
||||
### Using Hydra
|
||||
|
||||
Running `gdscript-hydra-show` (<kbd>C-c r</kbd>) opens a [hydra](https://github.com/abo-abo/hydra) popup with options to open the editor or run the project, a scene, or a script, including with visual debug options.
|
||||
|
||||
```
|
||||
d ( ) Debug p run project t run script h run from history a format all q quit
|
||||
e ( ) Editor s run scene r run last g switch to *godot* b format buffer
|
||||
|
||||
c [ ] Visible collisions shapes
|
||||
n [ ] Visible navigation
|
||||
```
|
||||
|
||||
### Formatting code with gdformat
|
||||
|
||||
You can call the `gdscript-format` function to format the current buffer with
|
||||
`gdformat`. This feature requires the python package `gdtoolkit` to be installed
|
||||
`gdformat`. Alternatively `gdscript-format-all` will reformat all gdscripts in
|
||||
the project. This feature requires the python package `gdtoolkit` to be installed
|
||||
and available on the system's PATH variable.
|
||||
|
||||
You can install gdtoolkit using the pip package manager from Python 3. Run this
|
||||
@@ -148,6 +177,48 @@ command in your shell to install it:
|
||||
pip3 install gdtoolkit
|
||||
```
|
||||
|
||||
### Browsing the Godot API with eww
|
||||
|
||||
With the point on a built-in class you can press `C-c C-b o` to open the code reference for that class in the text browser [eww](https://www.gnu.org/software/emacs/manual/html_node/emacs/EWW.html).
|
||||
|
||||
To open the main API reference page and browse it, press `C-c C-b a`.
|
||||
|
||||
#### Using a local copy of the Godot docs
|
||||
|
||||
You can browse the API reference offline with `eww`. To do so:
|
||||
|
||||
1. Get a build of the official documentation. You can build it from the [godot docs repository](https://github.com/godotengine/godot-docs/) or [download a build](https://hugo.pro/projects/godot-builds/) from Hugo Lourcio's website.
|
||||
2. Set `gdscript-docs-local-path` to the docs' directory, that contains the docs' `index.html` file.
|
||||
|
||||
For example:
|
||||
|
||||
```lisp
|
||||
(setq gdscript-docs-local-path "/home/gdquest/Documents/docs/godot")
|
||||
```
|
||||
|
||||
## Keyboard shortcuts
|
||||
|
||||
The following shortcuts are available by default:
|
||||
|
||||
- Inserting:
|
||||
- <kbd>C-c i</kbd> `gdscript-completion-insert-file-path-at-point`
|
||||
- Formatting:
|
||||
- <kbd>C-c C-f r</kbd> `gdscript-format-region`
|
||||
- <kbd>C-c C-f b</kbd> `gdscript-format-buffer`
|
||||
- Running the project and scenes in Godot:
|
||||
- <kbd>C-c C-r p</kbd> `gdscript-godot-open-project-in-editor`
|
||||
- <kbd>C-c C-r r</kbd> `gdscript-godot-run-project`
|
||||
- <kbd>C-c C-r d</kbd> `gdscript-godot-run-project-debug`
|
||||
- <kbd>C-c C-r s</kbd> `gdscript-godot-run-current-scene`
|
||||
- <kbd>C-c C-r q</kbd> `gdscript-godot-run-current-scene-debug`
|
||||
- <kbd>C-c C-r e</kbd> `gdscript-godot-edit-current-scene`
|
||||
- <kbd>C-c C-r x</kbd> `gdscript-godot-run-current-script`
|
||||
- Browsing the code reference:
|
||||
- <kbd>C-c C-b a</kbd> `gdscript-docs-browse-api`
|
||||
- <kbd>C-c C-b o</kbd> `gdscript-docs-browse-symbol-at-point`
|
||||
- Open hydra:
|
||||
- <kbd>C-c r</kbd> `gdscript-hydra-show` (require hydra package to be installed
|
||||
|
||||
## Customization
|
||||
|
||||
To find all GDScript-mode settings, press `M-x customize` and search for "gdscript".
|
||||
@@ -158,4 +229,5 @@ Code example:
|
||||
(setq gdscript-use-tab-indents t) ;; If true, use tabs for indents. Default: t
|
||||
(setq gdscript-indent-offset 4) ;; Controls the width of tab-based indents
|
||||
(setq gdscript-godot-executable "/path/to/godot") ;; Use this executable instead of 'godot' to open the Godot editor.
|
||||
(setq gdscript-gdformat-save-and-format t) ;; Save all buffers and format them with gdformat anytime Godot executable is run.
|
||||
```
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 202 KiB After Width: | Height: | Size: 209 KiB |
111
gdscript-comint-gdformat.el
Normal file
111
gdscript-comint-gdformat.el
Normal file
@@ -0,0 +1,111 @@
|
||||
;;; gdscript-comint-gdformat.el --- gdformat mode based on comint mode -*- lexical-binding: t; -*-
|
||||
;;
|
||||
;; Copyright (C) 2020 GDQuest and contributors
|
||||
;;
|
||||
;; Author: Josef Vlach <vlach.josef@gmail.com>
|
||||
;; URL: https://github.com/GDQuest/emacs-gdscript-mode/
|
||||
;; Version: 1.0.0
|
||||
;; Package-Requires: ((emacs "26.3"))
|
||||
;; Maintainer: nathan@gdquest.com
|
||||
;; Created: June 2020
|
||||
;; Keywords: languages
|
||||
;;
|
||||
;; This file is not part of GNU Emacs.
|
||||
;;
|
||||
;; 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:
|
||||
;;
|
||||
;; gdformat-mode for handling stdout and stderr from gdformat executable.
|
||||
;;
|
||||
;; It supports quick navigation from errors to file location.
|
||||
;;
|
||||
;;; Code:
|
||||
|
||||
(require 'ansi-color)
|
||||
(require 'comint)
|
||||
(require 'compile)
|
||||
(require 'gdscript-customization)
|
||||
(require 'gdscript-utils)
|
||||
|
||||
(defvar gdscript-comint-gdformat--mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(set-keymap-parent map
|
||||
(make-composed-keymap compilation-shell-minor-mode-map
|
||||
comint-mode-map))
|
||||
(define-key map (kbd "C-c r") 'gdscript-hydra-show)
|
||||
map)
|
||||
"Basic mode map for `gdformat-mode'.")
|
||||
|
||||
(defun gdscript-comint-gdformat--sentinel (process event)
|
||||
"Display result of formatting if gdformat PROCESS exited abnormal EVENT."
|
||||
(when (string-match "exited abnormally" event)
|
||||
(pop-to-buffer (process-buffer process))))
|
||||
|
||||
(defun gdscript-comint-gdformat--run (arguments)
|
||||
"Run gdformat in comint mode.
|
||||
|
||||
ARGUMENTS are command line arguments for gdformat executable.
|
||||
When run it will kill existing process if one exists."
|
||||
(let ((buffer-name (gdscript-util--get-gdformat-buffer-name))
|
||||
(inhibit-read-only t))
|
||||
|
||||
(when (not (executable-find gdscript-gdformat-executable))
|
||||
(error "Error: Could not find %s on PATH. Please customize the gdscript-gdformat-executable variable" gdscript-gdformat-executable))
|
||||
|
||||
(with-current-buffer (get-buffer-create buffer-name)
|
||||
(unless (derived-mode-p 'gdformat-mode)
|
||||
(gdformat-mode)
|
||||
(buffer-disable-undo))
|
||||
(erase-buffer)
|
||||
(let* ((line-length (list (format "--line-length=%s" gdscript-gdformat-line-length)))
|
||||
(buffer (comint-exec (current-buffer) buffer-name gdscript-gdformat-executable nil (append line-length arguments))))
|
||||
(set-process-sentinel (get-buffer-process buffer) 'gdscript-comint-gdformat--sentinel)
|
||||
buffer))))
|
||||
|
||||
(define-derived-mode gdformat-mode comint-mode "gdformat"
|
||||
"Major mode for gdformat.
|
||||
|
||||
\\{gdscript-comint-gdformat--mode-map}"
|
||||
(use-local-map gdscript-comint-gdformat--mode-map)
|
||||
(add-hook 'gdformat-mode-hook 'gdscript-comint-gdformat--initialize-for-comint-mode)
|
||||
(add-hook 'gdformat-mode-hook 'gdscript-comint-gdformat--initialize-for-compilation-mode))
|
||||
|
||||
(defun gdscript-comint-gdformat--initialize-for-comint-mode ()
|
||||
"Initialize buffer for comint mode support."
|
||||
(when (derived-mode-p 'comint-mode)
|
||||
(setq comint-process-echoes nil)
|
||||
(setq-local comint-buffer-maximum-size 4096)
|
||||
(setq-local comint-output-filter-functions '(ansi-color-process-output comint-postoutput-scroll-to-bottom))
|
||||
(setq ansi-color-for-comint-mode t)))
|
||||
|
||||
(defun gdscript-comint-gdformat--failed-file-name()
|
||||
"Find corresponding buffer name for error message: 'at line x col y'."
|
||||
(save-excursion
|
||||
(save-match-data
|
||||
(re-search-backward "exception during formatting of \\(.*\\)")
|
||||
(match-string-no-properties 1))))
|
||||
|
||||
(defun gdscript-comint-gdformat--initialize-for-compilation-mode ()
|
||||
"Initialize buffer for compilation mode support."
|
||||
(setq-local
|
||||
compilation-error-regexp-alist
|
||||
'(("at line \\([[:digit:]]+\\) col \\([[:digit:]]+\\)" gdscript-comint-gdformat--failed-file-name 1 2 nil nil)
|
||||
("exception during formatting of \\(.*\\)$" 1 nil nil nil 1)
|
||||
("reformatted \\(.*\\)$" 1 nil nil nil 1)))
|
||||
(setq-local compilation-mode-font-lock-keywords nil)
|
||||
(compilation-setup t))
|
||||
|
||||
(provide 'gdscript-comint-gdformat)
|
||||
;;; gdscript-comint-gdformat.el ends here
|
||||
113
gdscript-comint.el
Normal file
113
gdscript-comint.el
Normal file
@@ -0,0 +1,113 @@
|
||||
;;; gdscript-comint.el --- Support for comint mode -*- lexical-binding: t; -*-
|
||||
;;
|
||||
;; Copyright (C) 2020 GDQuest and contributors
|
||||
;;
|
||||
;; Author: Josef Vlach <vlach.josef@gmail.com>
|
||||
;; URL: https://github.com/GDQuest/emacs-gdscript-mode/
|
||||
;; Version: 1.0.0
|
||||
;; Package-Requires: ((emacs "26.3"))
|
||||
;; Maintainer: nathan@gdquest.com
|
||||
;; Created: May 2020
|
||||
;; Keywords: languages
|
||||
;;
|
||||
;; This file is not part of GNU Emacs.
|
||||
;;
|
||||
;; 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:
|
||||
;;
|
||||
;; godot-mode for handling stdout and stderr from godot executable.
|
||||
;;
|
||||
;; It supports quick navigation from errors to file location.
|
||||
;;
|
||||
;;; Code:
|
||||
|
||||
(require 'ansi-color)
|
||||
(require 'comint)
|
||||
(require 'compile)
|
||||
(require 'gdscript-customization)
|
||||
(require 'gdscript-format)
|
||||
(require 'gdscript-utils)
|
||||
|
||||
(defvar gdscript-comint--mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(set-keymap-parent map
|
||||
(make-composed-keymap compilation-shell-minor-mode-map
|
||||
comint-mode-map))
|
||||
(define-key map (kbd "C-a") 'comint-bol)
|
||||
(define-key map (kbd "C-c r") 'gdscript-hydra-show)
|
||||
map)
|
||||
"Basic mode map for `godot-mode'.")
|
||||
|
||||
(defun gdscript-comint--run (arguments)
|
||||
"Run godot in comint mode.
|
||||
|
||||
ARGUMENTS are command line arguments for godot executable.
|
||||
When run it will kill existing process if one exists."
|
||||
(let ((buffer-name (gdscript-util--get-godot-buffer-name (member "-e" arguments)))
|
||||
(inhibit-read-only t))
|
||||
(if (not (or (file-executable-p gdscript-godot-executable) (executable-find gdscript-godot-executable)))
|
||||
(error "Error: Could not execute '%s'. Please customize the `gdscript-godot-executable variable'" gdscript-godot-executable)
|
||||
(with-current-buffer (get-buffer-create buffer-name)
|
||||
(when gdscript-gdformat-save-and-format
|
||||
(gdscript-comint-gdformat--modified-buffers))
|
||||
(unless (derived-mode-p 'godot-mode)
|
||||
(godot-mode)
|
||||
(buffer-disable-undo))
|
||||
(erase-buffer)
|
||||
(comint-exec (current-buffer) buffer-name gdscript-godot-executable nil arguments)
|
||||
(set-process-sentinel (get-buffer-process (current-buffer)) 'gdscript-comint--sentinel)
|
||||
(pop-to-buffer (current-buffer))))))
|
||||
|
||||
(defun gdscript-comint--sentinel (process event)
|
||||
"Custom sentinel for PROCESS and EVENT.
|
||||
|
||||
Set process's buffer `inhibit-read-only' temporalily to value t,
|
||||
so that `internal-default-process-sentinel' can insert status
|
||||
message into the process’s buffer."
|
||||
(with-current-buffer (process-buffer process)
|
||||
(let ((inhibit-read-only t))
|
||||
(internal-default-process-sentinel process event))))
|
||||
|
||||
(define-derived-mode godot-mode comint-mode "godot"
|
||||
"Major mode for godot.
|
||||
|
||||
\\{gdscript-comint--mode-map}"
|
||||
(use-local-map gdscript-comint--mode-map)
|
||||
(add-hook 'godot-mode-hook 'gdscript-comint--initialize-for-comint-mode)
|
||||
(add-hook 'godot-mode-hook 'gdscript-comint--initialize-for-compilation-mode))
|
||||
|
||||
(defun gdscript-comint--initialize-for-comint-mode ()
|
||||
"Initialize buffer for comint mode support."
|
||||
(when (derived-mode-p 'comint-mode)
|
||||
(setq comint-process-echoes nil)
|
||||
(setq comint-prompt-regexp "^debug> ")
|
||||
(setq-local comint-use-prompt-regexp t)
|
||||
(setq-local comint-prompt-read-only t)
|
||||
(setq-local comint-buffer-maximum-size 4096)
|
||||
(setq-local comint-output-filter-functions '(ansi-color-process-output comint-postoutput-scroll-to-bottom))
|
||||
(setq ansi-color-for-comint-mode t)))
|
||||
|
||||
(defun gdscript-comint--initialize-for-compilation-mode ()
|
||||
"Initialize buffer for compilation mode support."
|
||||
(setq-local
|
||||
compilation-error-regexp-alist
|
||||
'(
|
||||
("^ At: res://\\([-_[:word:]\/]+.gd\\):\\([[:digit:]]+\\)." 1 2 nil 2 1)
|
||||
("^*Frame [[:digit:]]+ - res://\\([-_[:word:]\/]+.gd\\):\\([[:digit:]]+\\)." 1 2 nil 2 1)))
|
||||
(setq-local compilation-mode-font-lock-keywords nil)
|
||||
(compilation-setup t))
|
||||
|
||||
(provide 'gdscript-comint)
|
||||
;;; gdscript-comint.el ends here
|
||||
@@ -33,6 +33,8 @@
|
||||
;;; Code:
|
||||
|
||||
(require 'gdscript-syntax)
|
||||
(require 'gdscript-utils)
|
||||
(require 'projectile nil t)
|
||||
|
||||
(defvar-local gdscript-completion--all-keywords
|
||||
(eval-when-compile (append gdscript-keywords gdscript-built-in-classes
|
||||
@@ -48,6 +50,38 @@
|
||||
(list start end gdscript-completion--all-keywords
|
||||
. nil)))
|
||||
|
||||
(defun gdscript-completion-insert-file-path-at-point (&optional arg)
|
||||
"Insert a file path at point using Godot's relative path (\"res:\").
|
||||
|
||||
If Projectile is available, list only the files in the current
|
||||
project. Otherwise, fallback to the built-in function
|
||||
`read-file-name'.
|
||||
|
||||
If using Projectile, with a prefix ARG invalidates the cache
|
||||
first."
|
||||
(interactive "P")
|
||||
(let ((has-projectile (featurep 'projectile)))
|
||||
(when has-projectile
|
||||
(projectile-maybe-invalidate-cache arg))
|
||||
(let* ((project-root
|
||||
(if has-projectile
|
||||
(projectile-ensure-project (projectile-project-root))
|
||||
(gdscript-util--find-project-configuration-file)))
|
||||
(file
|
||||
(if has-projectile
|
||||
(projectile-completing-read
|
||||
"Find file: "
|
||||
(projectile-project-files project-root))
|
||||
(read-file-name
|
||||
"Find file: "
|
||||
project-root))))
|
||||
(when file
|
||||
(insert
|
||||
(concat "\"res://"
|
||||
(gdscript-util--get-godot-project-file-path-relative file)
|
||||
"." (file-name-extension file) "\""))))))
|
||||
|
||||
|
||||
(provide 'gdscript-completion)
|
||||
|
||||
;;; gdscript-completion.el ends here
|
||||
|
||||
@@ -58,19 +58,6 @@
|
||||
:type 'integer
|
||||
:safe 'integerp)
|
||||
|
||||
(defcustom gdscript-indent-guess-indent-offset
|
||||
t "If t, tells GDScript mode to guess `gdscript-indent-offset' value."
|
||||
:type 'boolean
|
||||
:group 'gdscript
|
||||
:safe 'booleanp)
|
||||
|
||||
(defcustom gdscript-indent-guess-indent-offset-verbose
|
||||
t "If t, emit a warning when guessing indentation fails."
|
||||
:version "25.1"
|
||||
:type 'boolean
|
||||
:group 'gdscript
|
||||
:safe 'booleanp)
|
||||
|
||||
(defcustom gdscript-indent-trigger-commands '(indent-for-tab-command yas-expand yas/expand)
|
||||
"Commands that might trigger a `gdscript-indent-line' call."
|
||||
:type '(repeat symbol):group'gdscript)
|
||||
@@ -104,11 +91,54 @@ fill parens."
|
||||
"Multiplier applied to indentation inside multi-line def blocks."
|
||||
:version "26.1"
|
||||
:type 'integer
|
||||
:safe 'natnump)
|
||||
:safe 'natnump)
|
||||
|
||||
(defcustom gdscript-godot-executable "godot"
|
||||
"The godot executable which is either a full path such as '~/bin/godot2.2'
|
||||
or the name of an executable on the system PATH (usually 'godot')"
|
||||
:type 'string
|
||||
:group 'gdscript)
|
||||
|
||||
(defcustom gdscript-gdformat-executable "gdformat"
|
||||
"The path to the gdformat executable.
|
||||
By default, it assumes that the executable is in the system's
|
||||
PATH."
|
||||
:type 'string
|
||||
:group 'gdscript)
|
||||
|
||||
(defcustom gdscript-gdformat-line-length 100
|
||||
"How many characters per line to allow when formatting gdscript by gdformat."
|
||||
:type 'integer
|
||||
:group 'gdscript)
|
||||
|
||||
(defcustom gdscript-gdformat-save-and-format nil
|
||||
"If t, save all modified buffers and format them with gdformat.
|
||||
It happens anytime Godot executable is run. Formatting runs on background,
|
||||
so it is not slowing down Godot execution."
|
||||
:type 'boolean
|
||||
:group 'gdscript)
|
||||
|
||||
(defcustom gdscript-docs-force-online-lookup nil
|
||||
"If true, calling commands like gdscript-docs-browse-api browses the online API reference, even if a local copy is available."
|
||||
:type 'boolean
|
||||
:group 'gdscript)
|
||||
|
||||
(defcustom gdscript-docs-use-eww t
|
||||
"If set to false use the emacs configurable `browse-url' function rather than `eww' directly. `browse-url' can be configured to open the desktop default GUI browser, for example, via the variable `browse-url-browser-function'"
|
||||
:type 'boolean
|
||||
:group 'gdscript)
|
||||
|
||||
(defcustom gdscript-docs-local-path ""
|
||||
"Optional path to a local build of the Godot documentation.
|
||||
If not set to an empty string, the commands `gdscript-docs-browse-api'
|
||||
and `gdscript-docs-browse-symbol-at-point' allow you to browse the local files.
|
||||
Must be the root directory of the website, that is to say, a
|
||||
directory path containing the file `index.html'."
|
||||
:type 'string
|
||||
:group 'gdscript)
|
||||
|
||||
(defcustom gdscript-docs-online-search-api-url "https://docs.godotengine.org/en/stable/search.html?q=%s&check_keywords=yes&area=default"
|
||||
"Online Godot API search url"
|
||||
:type 'string
|
||||
:group 'gdscript)
|
||||
|
||||
|
||||
138
gdscript-docs.el
Normal file
138
gdscript-docs.el
Normal file
@@ -0,0 +1,138 @@
|
||||
;;; gdscript-docs.el --- Open documentation in Godot -*- lexical-binding: t; -*-
|
||||
;;
|
||||
;; Copyright (C) 2020 GDQuest and contributors
|
||||
;;
|
||||
;; Author: Josef Vlach <vlach.josef@gmail.com>
|
||||
;; URL: https://github.com/GDQuest/emacs-gdscript-mode/
|
||||
;; Version: 1.0.0
|
||||
;; Package-Requires: ((emacs "26.3"))
|
||||
;; Maintainer: nathan@gdquest.com
|
||||
;; Created: May 2020
|
||||
;; Keywords: languages
|
||||
;;
|
||||
;; This file is not part of GNU Emacs.
|
||||
;;
|
||||
;; 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:
|
||||
;;
|
||||
;; Browse the Godot API reference in the text-based browser eww.
|
||||
;;
|
||||
;;; Code:
|
||||
|
||||
(require 'eww)
|
||||
(require 'gdscript-customization)
|
||||
|
||||
(defun gdscript-docs-open (url &optional)
|
||||
"when `gdscript-docs-use-eww' is true use `eww' else use `browse-url'"
|
||||
(if gdscript-docs-use-eww
|
||||
(if (file-exists-p url) (eww-open-file url) (eww-browse-url url t))
|
||||
(browse-url url)))
|
||||
|
||||
;;;###autoload
|
||||
(defun gdscript-docs-browse-api (&optional force-online)
|
||||
"Open the main page of Godot API. Use the universal prefix (C-u) to force browsing the online API."
|
||||
(interactive)
|
||||
(if (and (or gdscript-docs-force-online-lookup current-prefix-arg force-online) (not (string= gdscript-docs-local-path "")))
|
||||
(gdscript-docs-open "https://docs.godotengine.org/en/stable/classes/index.html?#godot-api")
|
||||
(let ((file (concat (file-name-as-directory gdscript-docs-local-path) "classes/index.html")))
|
||||
(if (file-exists-p file)
|
||||
(gdscript-docs-open file)
|
||||
(message "\"%s\" not found" file)))))
|
||||
|
||||
(defun gdscript-docs-browse-symbol-at-point (&optional force-online)
|
||||
"Open the API reference for the symbol at point in the browser eww.
|
||||
If a page is already open, switch to its buffer. Use local docs if gdscripts-docs-local-path set. Use the universal prefix (C-u) to force browsing the online API."
|
||||
(interactive)
|
||||
|
||||
(let* ((symbol-at-point (thing-at-point 'symbol t))
|
||||
(symbol (if symbol-at-point (downcase symbol-at-point) ""))
|
||||
(buffer (if (not gdscript-docs-use-eww) nil
|
||||
(seq-find
|
||||
(lambda (current-buffer)
|
||||
(with-current-buffer current-buffer
|
||||
(when (derived-mode-p 'eww-mode)
|
||||
(string-suffix-p symbol(string-remove-suffix ".html" (plist-get eww-data :url)) t)
|
||||
))) (buffer-list)))))
|
||||
(if buffer (pop-to-buffer-same-window buffer)
|
||||
(if (string= "" symbol)
|
||||
(message "No symbol at point or open API reference buffers.")
|
||||
(if (and (not gdscript-docs-force-online-lookup)(not (or current-prefix-arg force-online)) (not (string= gdscript-docs-local-path "")))
|
||||
(let ((file (concat (file-name-as-directory gdscript-docs-local-path) (file-name-as-directory "classes") "class_" symbol ".html")))
|
||||
(if (file-exists-p file)
|
||||
(gdscript-docs-open file)
|
||||
(message "No local API help for \"%s\"." symbol)))
|
||||
(let ((url (format "https://docs.godotengine.org/en/stable/classes/class_%s.html#%s" symbol symbol)))
|
||||
(gdscript-docs-open url)))))))
|
||||
|
||||
(defun gdscript-docs-online-search-api (&optional sym)
|
||||
"Search Godot docs online. Use the universal prefix (C-u) to prompt for search term."
|
||||
(interactive)
|
||||
(let ((symbol (if current-prefix-arg (read-string "API Search: ") (or sym (thing-at-point 'symbol t) ""))))
|
||||
(browse-url (format gdscript-docs-online-search-api-url (downcase symbol)))))
|
||||
|
||||
|
||||
(defun gdscript-docs--rename-eww-buffer ()
|
||||
"Rename the eww buffer visiting the Godot documentation.
|
||||
Rename the buffer from a generic name to a name based on the web page's title."
|
||||
(when (derived-mode-p 'eww-mode)
|
||||
(let ((title (plist-get eww-data :title)))
|
||||
(when (string-match "Godot Engine" title)
|
||||
(rename-buffer (format "*eww - %s*" title) t)))))
|
||||
|
||||
(defun gdscript-docs--filter-content-to-main-div ()
|
||||
"Filters a page in the Godot docs down to its main <div>.
|
||||
|
||||
This is a re-implementation of `eww-readable'."
|
||||
(let* ((old-data eww-data)
|
||||
(dom (with-temp-buffer
|
||||
(insert (plist-get old-data :source))
|
||||
(condition-case nil
|
||||
(decode-coding-region (point-min) (point-max) 'utf-8)
|
||||
(coding-system-error nil))
|
||||
(libxml-parse-html-region (point-min) (point-max))))
|
||||
(base (plist-get eww-data :url))
|
||||
;; Filters the page down to the main div: <div role="main"> ... </div>
|
||||
(main (dom-elements dom 'role "main")))
|
||||
(eww-display-html nil nil
|
||||
(list 'base (list (cons 'href base))
|
||||
main)
|
||||
nil (current-buffer))
|
||||
(dolist (elem '(:source :url :title :next :previous :up))
|
||||
(plist-put eww-data elem
|
||||
(plist-get old-data elem)))
|
||||
(eww-update-header-line-format)))
|
||||
|
||||
(defun gdscript-docs--eww-follow-link (orig-fun &rest args)
|
||||
"Remember url when following local link on a page.
|
||||
|
||||
ORIG-FUN is function we wrap around. ARGS are argument to ORIG-FUN function."
|
||||
(let ((url (plist-get eww-data :url))
|
||||
(res (apply orig-fun args)))
|
||||
(plist-put eww-data :url url)
|
||||
res))
|
||||
|
||||
(defun gdscript-docs--eww-setup ()
|
||||
"Convenience setup for pages with Godot documentation."
|
||||
(when (string-match "docs.godotengine" (plist-get eww-data :url))
|
||||
(setq multi-isearch-next-buffer-function nil)
|
||||
(gdscript-docs--rename-eww-buffer)
|
||||
(gdscript-docs--filter-content-to-main-div)))
|
||||
|
||||
(advice-add 'eww-follow-link :around #'gdscript-docs--eww-follow-link)
|
||||
(add-hook 'eww-after-render-hook #'gdscript-docs--eww-setup)
|
||||
|
||||
(provide 'gdscript-docs)
|
||||
|
||||
;;; gdscript-docs.el ends here
|
||||
@@ -31,65 +31,72 @@
|
||||
|
||||
;;; Code:
|
||||
|
||||
(defun gdscript-format--run-gdformat (buffer-input buffer-output buffer-error)
|
||||
"Call gdformat process.
|
||||
Argument BUFFER-INPUT reference to the input buffer to format.
|
||||
Argument BUFFER-OUTPUT the buffer to write the output of the gdformat call.
|
||||
Argument BUFFER-ERROR the buffer to write errors to."
|
||||
(with-current-buffer buffer-input
|
||||
(let ((process (make-process :name "gdformat"
|
||||
:command (list "gdformat" "-"):buffer
|
||||
buffer-output
|
||||
:stderr buffer-error
|
||||
:noquery t
|
||||
:sentinel (lambda (_process _event)))))
|
||||
(set-process-query-on-exit-flag (get-buffer-process buffer-error)
|
||||
nil)
|
||||
(set-process-sentinel (get-buffer-process buffer-error)
|
||||
(lambda (_process _event)))
|
||||
(save-restriction (widen)
|
||||
(process-send-region process
|
||||
(point-min)
|
||||
(point-max)))
|
||||
(process-send-eof process)
|
||||
(accept-process-output process nil nil t)
|
||||
(while (process-live-p process)
|
||||
(accept-process-output process nil nil t))
|
||||
(process-exit-status process))))
|
||||
(require 'gdscript-comint-gdformat)
|
||||
|
||||
(defun gdscript-format-buffer ()
|
||||
"Formats current buffer using 'gdformat'."
|
||||
(defmacro gdscript-format--save-buffer (&rest body)
|
||||
"Execute the forms in BODY if current buffer is gdscript.
|
||||
|
||||
It also activates `auto-revert-mode' and saves the buffer if is it modified."
|
||||
(declare (indent 1) (debug t))
|
||||
`(when (and (buffer-file-name)
|
||||
(string-match ".*.gd$" (buffer-file-name)))
|
||||
(unless (bound-and-true-p auto-revert-mode)
|
||||
(auto-revert-mode))
|
||||
(when (buffer-modified-p)
|
||||
(save-buffer))
|
||||
,@body))
|
||||
|
||||
(defmacro gdscript-format--with-gdscripts (gdscript-buffers &rest body)
|
||||
"Execute the forms in BODY with GDSCRIPT-BUFFERS containing all gdscript buffers currently open."
|
||||
(declare (indent 1) (debug t))
|
||||
`(progn
|
||||
(dolist (buffer (buffer-list))
|
||||
(with-current-buffer buffer
|
||||
(gdscript-format--save-buffer
|
||||
(push (buffer-file-name) ,gdscript-buffers))))
|
||||
,@body))
|
||||
|
||||
(defun gdscript-format--format-region (start end)
|
||||
"Format the region between START and END using `gdformat'."
|
||||
(let
|
||||
((cmd (concat "echo " (shell-quote-argument (buffer-substring start end)) "|" gdscript-gdformat-executable " -"))
|
||||
(error-buffer "*gdformat-errors*"))
|
||||
(if (eq (with-temp-buffer (call-process-shell-command cmd)) 0)
|
||||
(save-excursion
|
||||
(shell-command-on-region start end cmd (buffer-name) t error-buffer t))
|
||||
(progn
|
||||
(with-current-buffer error-buffer (erase-buffer))
|
||||
(shell-command-on-region start end cmd nil nil error-buffer t)))))
|
||||
|
||||
(defun gdscript-format-region()
|
||||
"Format the selected region using `gdformat'."
|
||||
(interactive)
|
||||
(let* ((buffer-start (current-buffer))
|
||||
(point-start (point))
|
||||
(window-pos-start (window-start))
|
||||
(buffer-temp (get-buffer-create "*gdformat*"))
|
||||
(buffer-error (get-buffer-create "*gdformat-error*")))
|
||||
(dolist (buf (list buffer-temp buffer-error))
|
||||
(with-current-buffer buf
|
||||
(erase-buffer)))
|
||||
(condition-case err
|
||||
(if (/= 0 (gdscript-format--run-gdformat buffer-start buffer-temp
|
||||
buffer-error))
|
||||
(error "GDSCript formatter: failed, see buffer %s for details"
|
||||
(buffer-name buffer-error))
|
||||
(if (/= 0 (compare-buffer-substrings buffer-temp nil
|
||||
nil buffer-start nil nil))
|
||||
(progn
|
||||
(with-current-buffer buffer-temp
|
||||
(copy-to-buffer buffer-start
|
||||
(point-min)
|
||||
(point-max)))
|
||||
(goto-char point-start)
|
||||
(set-window-start (selected-window)
|
||||
window-pos-start)
|
||||
(message "gdscript formatter: success"))
|
||||
(message "gdscript formatter: nothing to do"))
|
||||
(mapc 'kill-buffer
|
||||
(list buffer-temp buffer-error)))
|
||||
(error (message "%s"
|
||||
(error-message-string err))
|
||||
(pop-to-buffer buffer-error)))))
|
||||
(gdscript-format--format-region
|
||||
(region-beginning) (region-end)))
|
||||
|
||||
(defun gdscript-format-buffer()
|
||||
"Format the current buffer using `gdformat'."
|
||||
(interactive)
|
||||
(gdscript-format--save-buffer
|
||||
(gdscript-comint-gdformat--run (list (buffer-file-name)))))
|
||||
|
||||
(defun gdscript-comint-gdformat--modified-buffers ()
|
||||
"Save and format all modified buffers using `gdformat'."
|
||||
(let ((gdscript-buffers))
|
||||
(gdscript-format--with-gdscripts gdscript-buffers
|
||||
(when gdscript-buffers
|
||||
(gdscript-comint-gdformat--run gdscript-buffers)))))
|
||||
|
||||
(defun gdscript-format-all()
|
||||
"Save modified buffers and then format all gdscripts in the project."
|
||||
(interactive)
|
||||
(let ((gdscript-buffers))
|
||||
(gdscript-format--with-gdscripts gdscript-buffers
|
||||
(let* ((rl (gdscript-util--find-project-configuration-file))
|
||||
(gdscript-file-list (directory-files-recursively rl ".*.gd$" t)))
|
||||
(let ((all-gdscripts (delete-dups (append gdscript-file-list gdscript-buffers))))
|
||||
(when all-gdscripts
|
||||
(pop-to-buffer (gdscript-comint-gdformat--run all-gdscripts))))))))
|
||||
|
||||
(provide 'gdscript-format)
|
||||
|
||||
|
||||
@@ -32,76 +32,98 @@
|
||||
;;
|
||||
;;; Code:
|
||||
|
||||
(require 'gdscript-comint)
|
||||
(require 'gdscript-customization)
|
||||
(require 'gdscript-history)
|
||||
(require 'gdscript-project)
|
||||
(require 'gdscript-utils)
|
||||
|
||||
;;;###autoload
|
||||
(defun gdscript-run-command (cmd &optional show)
|
||||
"Run a Godot process.
|
||||
Input and output via buffer named after `*godot*'. If there is a process
|
||||
already running in that buffer, just switch to it.
|
||||
(defvar gdscript-godot--debug-options-hydra :not-list)
|
||||
|
||||
With argument, allows you to define CMD so you can edit the
|
||||
command used to call the interpreter.
|
||||
When numeric prefix arg is other than 0 or 4 do not SHOW."
|
||||
(interactive
|
||||
(if current-prefix-arg
|
||||
(list
|
||||
(read-string "Run Godot: " (gdscript-godot--build-shell-command))
|
||||
(y-or-n-p "Make dedicated process? ")
|
||||
(= (prefix-numeric-value current-prefix-arg) 4))
|
||||
(list (gdscript-godot--build-shell-command) nil t)))
|
||||
(start-process-shell-command "Godot Process" (if show
|
||||
"*godot*" nil) cmd))
|
||||
(defvar gdscript-godot--debug-selected-option 1)
|
||||
|
||||
(defvar gdscript-godot--debug-options-alist
|
||||
'((1 . ())
|
||||
(2 . ("--debug-collisions"))
|
||||
(3 . ("--debug-navigation"))
|
||||
(4 . ("--debug-collisions" "--debug-navigation"))))
|
||||
|
||||
(defmacro gdscript-godot--debug-options-handler (debug-options &rest body)
|
||||
"Execute the forms in BODY with DEBUG-OPTIONS being set.
|
||||
|
||||
DEBUG-OPTIONS will be set either by Hydra, or by prefix argument selection."
|
||||
(declare (indent 1) (debug t))
|
||||
`(let* ((debug-option-index
|
||||
(if current-prefix-arg
|
||||
(gdscript-godot--change-debug-options)
|
||||
gdscript-godot--debug-selected-option))
|
||||
(prefix-options (cdr (assoc debug-option-index gdscript-godot--debug-options-alist)))
|
||||
(use-hydra-options (listp gdscript-godot--debug-options-hydra)) ;; gdscript-godot--debug-options-hydra is a list when run from hydra
|
||||
(,debug-options (if use-hydra-options gdscript-godot--debug-options-hydra prefix-options)))
|
||||
,@body))
|
||||
|
||||
(defun gdscript-godot--run-command (&rest arguments)
|
||||
"Run a Godot process with ARGUMENTS.
|
||||
|
||||
The output of the process will be provided in a buffer named
|
||||
`*godot - <project-name>*'."
|
||||
(let ((args (gdscript-util--flatten arguments)))
|
||||
(gdscript-history--add-to-history args)
|
||||
(gdscript-comint--run (append (gdscript-godot--build-shell-command) args))
|
||||
(setq gdscript-godot--debug-options-hydra :not-list)))
|
||||
|
||||
(defun gdscript-godot--build-shell-command (&optional path)
|
||||
"Build a shell command to with the Godot executable.
|
||||
|
||||
If PATH is not provided, try to find it using the current
|
||||
file's directory as starting point."
|
||||
(let* ((project-path (or path (gdscript-util--find-project-configuration-file))))
|
||||
(concat gdscript-godot-executable " --path " project-path)))
|
||||
(let ((project-path (or path (gdscript-util--find-project-configuration-file))))
|
||||
(list "--path" project-path)))
|
||||
|
||||
(defun gdscript-godot-open-project-in-editor ()
|
||||
"Run Godot Engine Editor."
|
||||
(interactive)
|
||||
(gdscript-run-command
|
||||
(concat (gdscript-godot--build-shell-command) " -e")))
|
||||
(gdscript-godot--run-command "-e"))
|
||||
|
||||
(defun gdscript-godot-run-project ()
|
||||
"Run the current project in Godot Engine."
|
||||
(interactive)
|
||||
(let* ()
|
||||
(gdscript-run-command
|
||||
(gdscript-godot--build-shell-command))))
|
||||
(gdscript-godot--run-command))
|
||||
|
||||
(defun gdscript-godot-run-project-debug ()
|
||||
"Run the current project in Godot Engine."
|
||||
"Run the current project in Godot Engine.
|
||||
|
||||
When run with prefix argument, it offers extra debug options to choose from."
|
||||
(interactive)
|
||||
(let* ()
|
||||
(gdscript-run-command
|
||||
(concat (gdscript-godot--build-shell-command) " -d"))))
|
||||
(gdscript-godot--debug-options-handler debug-options
|
||||
(gdscript-godot--run-command "-d" debug-options)))
|
||||
|
||||
(defun gdscript-godot-run-current-scene ()
|
||||
"Run the current script file in Godot Engine."
|
||||
"Run the current script file in Godot Engine. Use the universal prefix (C-u) to force a scene select."
|
||||
(interactive)
|
||||
(gdscript-run-command
|
||||
(concat (gdscript-godot--build-shell-command) " "
|
||||
(gdscript-util--get-godot-project-file-path-relative buffer-file-name) ".tscn")))
|
||||
(gdscript-godot--run-command (gdscript-godot--select-scene current-prefix-arg)))
|
||||
|
||||
(defun gdscript-godot-run-current-scene-debug ()
|
||||
"Run the current script file in Godot Engine."
|
||||
"Run the current script file in Godot Engine.
|
||||
|
||||
When run with prefix argument, it offers extra debug options to choose from."
|
||||
(interactive)
|
||||
(gdscript-run-command
|
||||
(concat (gdscript-godot--build-shell-command) " -d "
|
||||
(gdscript-util--get-godot-project-file-path-relative buffer-file-name) ".tscn")))
|
||||
(gdscript-godot--debug-options-handler debug-options
|
||||
(gdscript-godot--run-command "-d" debug-options (gdscript-godot--select-scene))))
|
||||
|
||||
(defun gdscript-godot-edit-current-scene ()
|
||||
"Run the current script file in Godot Engine."
|
||||
(interactive)
|
||||
(gdscript-run-command
|
||||
(concat (gdscript-godot--build-shell-command) " -e "
|
||||
(gdscript-util--get-godot-project-file-path-relative buffer-file-name) ".tscn")))
|
||||
(gdscript-godot--run-command "-e" (gdscript-godot--select-scene current-prefix-arg)))
|
||||
|
||||
(defun gdscript-godot--select-scene (&optional select-scene)
|
||||
"Select scene to run"
|
||||
(if select-scene
|
||||
(gdscript-project--select-scene)
|
||||
(let ((scene-name (gdscript-project--current-buffer-scene)))
|
||||
(if scene-name scene-name
|
||||
(gdscript-project--select-scene)))))
|
||||
|
||||
(defun gdscript-godot-run-current-script ()
|
||||
"Run the current script file in Godot Engine.
|
||||
@@ -109,8 +131,35 @@ file's directory as starting point."
|
||||
For this to work, the script must inherit either from
|
||||
\"SceneTree\" or \"MainLoop\"."
|
||||
(interactive)
|
||||
(gdscript-run-command
|
||||
(concat (gdscript-godot--build-shell-command) " -s " (file-relative-name buffer-file-name))))
|
||||
(let ((script-name (gdscript-project--current-buffer-script)))
|
||||
(if script-name
|
||||
(gdscript-godot--run-script script-name)
|
||||
(gdscript-project--get-all-scripts))))
|
||||
|
||||
(defun gdscript-godot--run-script (script-name)
|
||||
"Run SCRIPT-NAME script in Godot Engine."
|
||||
(gdscript-godot--run-command "-s" script-name))
|
||||
|
||||
(defun gdscript-godot--debug-options-to-string (index)
|
||||
"Return debug options from `gdscript-godot--debug-options-alist' for given INDEX as a string."
|
||||
(mapconcat 'identity (cdr (assoc index gdscript-godot--debug-options-alist)) " "))
|
||||
|
||||
(defun gdscript-godot--debug-options-collection ()
|
||||
"Output a list of debug options to choose from by *-read function."
|
||||
(list
|
||||
(format "1) [%s] <no options>" (if (eq gdscript-godot--debug-selected-option 1) "X" " "))
|
||||
(format "2) [%s] %s" (if (eq gdscript-godot--debug-selected-option 2) "X" " ") (gdscript-godot--debug-options-to-string 2))
|
||||
(format "3) [%s] %s" (if (eq gdscript-godot--debug-selected-option 3) "X" " ") (gdscript-godot--debug-options-to-string 3))
|
||||
(format "4) [%s] %s" (if (eq gdscript-godot--debug-selected-option 4) "X" " ") (gdscript-godot--debug-options-to-string 4))))
|
||||
|
||||
(defun gdscript-godot--change-debug-options ()
|
||||
"Read debug option and parse it as a number.
|
||||
|
||||
Once read it is saved in `gdscript-godot--debug-selected-option'
|
||||
variable for later use."
|
||||
(let* ((option (gdscript-util--read (gdscript-godot--debug-options-collection)))
|
||||
(index (string-to-number option)))
|
||||
(setq gdscript-godot--debug-selected-option index)))
|
||||
|
||||
(provide 'gdscript-godot)
|
||||
;;; gdscript-godot.el ends here
|
||||
|
||||
94
gdscript-history.el
Normal file
94
gdscript-history.el
Normal file
@@ -0,0 +1,94 @@
|
||||
;;; gdscript-history.el --- History -*- lexical-binding: t; -*-
|
||||
;;
|
||||
;; Copyright (C) 2020 GDQuest and contributors
|
||||
;;
|
||||
;; Author: Josef Vlach <vlach.josef@gmail.com>
|
||||
;; URL: https://github.com/GDQuest/emacs-gdscript-mode/
|
||||
;; Version: 1.0.0
|
||||
;; Package-Requires: ((emacs "26.3"))
|
||||
;; Maintainer: nathan@gdquest.com
|
||||
;; Created: June 2020
|
||||
;; Keywords: languages
|
||||
;;
|
||||
;; This file is not part of GNU Emacs.
|
||||
;;
|
||||
;; 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:
|
||||
;;
|
||||
;; Keep history of commands for later quick execution.
|
||||
;;
|
||||
;;; Code:
|
||||
|
||||
(require 'gdscript-utils)
|
||||
|
||||
(defvar gdscript-history--previous-arguments-plist nil
|
||||
"Holds history of commands per project.")
|
||||
|
||||
(defmacro gdscript-history--with-project-history (project-history &rest body)
|
||||
"Execute the forms in BODY with PROJECT-HISTORY being set.
|
||||
|
||||
PROJECT-HISTORY will be history of current project."
|
||||
(declare (indent 1) (debug t))
|
||||
`(let* ((property (gdscript-util--find-project-configuration-file))
|
||||
(,project-history (lax-plist-get gdscript-history--previous-arguments-plist property)))
|
||||
,@body))
|
||||
|
||||
(defun gdscript-history--add-to-history (arguments)
|
||||
"Add ARGUMENTS to history for current project."
|
||||
(gdscript-history--with-project-history history
|
||||
(push arguments history)
|
||||
(delete-dups history)
|
||||
(setq gdscript-history--previous-arguments-plist
|
||||
(lax-plist-put gdscript-history--previous-arguments-plist property history))))
|
||||
|
||||
(defun gdscript-history--last-command ()
|
||||
"Return last command from history."
|
||||
(gdscript-history--with-project-history history
|
||||
(car history)))
|
||||
|
||||
(defun gdscript-history--line-data (command)
|
||||
"Pretty print COMMAND."
|
||||
(let* ((flat (gdscript-util--flatten command))
|
||||
(l (car (last flat))))
|
||||
(cond ((string-suffix-p ".tscn" l)
|
||||
(progn
|
||||
(string-match "\\(.*/\\)?\\(.*\\).tscn$" l)
|
||||
(list (format "Scene %s.tscn: %s " (match-string-no-properties 2 l) l) (butlast command))))
|
||||
((string-suffix-p ".gd" l)
|
||||
(progn
|
||||
(string-match "\\(.*/\\)?\\(.*\\).gd$" l)
|
||||
(list (format "Script %s.gd: %s " (match-string-no-properties 2 l) l) (butlast command))))
|
||||
(t
|
||||
(list (format "Project %s: " (gdscript-util--get-godot-project-name)) command)))))
|
||||
|
||||
(defun gdscript-history--line (command idx)
|
||||
"Render COMMAND as string starting with index IDX."
|
||||
(let* ((index (number-to-string (1+ idx)))
|
||||
(data (gdscript-history--line-data command))
|
||||
(name (car data))
|
||||
(args (cadr data)))
|
||||
(string-trim (concat index ") " name (mapconcat 'identity args " ")))))
|
||||
|
||||
(defun gdscript-history--select-from-history ()
|
||||
"Choose command from history."
|
||||
(gdscript-history--with-project-history history
|
||||
(let* ((history-data (seq-map-indexed #'gdscript-history--line history))
|
||||
(selected (gdscript-util--read history-data "Run again"))
|
||||
(index (string-to-number selected)))
|
||||
(nth (1- index) history))))
|
||||
|
||||
(provide 'gdscript-history)
|
||||
|
||||
;;; gdscript-history.el ends here
|
||||
156
gdscript-hydra.el
Normal file
156
gdscript-hydra.el
Normal file
@@ -0,0 +1,156 @@
|
||||
;;; gdscript-hydra.el --- Hydra for launching Godot -*- lexical-binding: t; -*-
|
||||
;;
|
||||
;; Copyright (C) 2020 GDQuest and contributors
|
||||
;;
|
||||
;; Author: Josef Vlach <vlach.josef@gmail.com>
|
||||
;; URL: https://github.com/GDQuest/emacs-gdscript-mode/
|
||||
;; Version: 1.0.0
|
||||
;; Package-Requires: ((emacs "26.3"))
|
||||
;; Maintainer: nathan@gdquest.com
|
||||
;; Created: May 2020
|
||||
;; Keywords: languages
|
||||
;;
|
||||
;; This file is not part of GNU Emacs.
|
||||
;;
|
||||
;; 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:
|
||||
;;
|
||||
;; Hydra for launching Godot. It offers options to launch Godot in debug mode or open editor or run the script.
|
||||
;;
|
||||
;; When running in debug mode it offers two additional flags to pass to Godot:
|
||||
;; --debug-collisions
|
||||
;; --debug-navigation
|
||||
;;
|
||||
;;; Code:
|
||||
|
||||
(require 'hydra nil t)
|
||||
(require 'gdscript-format)
|
||||
(require 'gdscript-godot)
|
||||
(require 'gdscript-history)
|
||||
(require 'gdscript-utils)
|
||||
|
||||
;;;###autoload
|
||||
(defvar gdscript-hydra--open nil)
|
||||
(defvar gdscript-hydra--debug nil)
|
||||
(defvar gdscript-hydra--editor nil)
|
||||
(defvar gdscript-hydra--debug-collisions nil)
|
||||
(defvar gdscript-hydra--debug-navigation nil)
|
||||
|
||||
(defun gdscript-hydra-show ()
|
||||
"Show gdcript hydra."
|
||||
(interactive)
|
||||
(when (not (featurep 'hydra))
|
||||
(error "No `hydra.el' available. To execute `gdscript-hydra-show' command you need to install hydra.el"))
|
||||
(gdscript-hydra--menu/body))
|
||||
|
||||
(defun gdscript-hydra--selected (selected)
|
||||
"Visual representation for (non)SELECTED checkboxes."
|
||||
(if selected "x" " "))
|
||||
|
||||
(defun gdscript-hydra--dispatch (run-default run-debug run-editor)
|
||||
"Run Godot with selected flag.
|
||||
|
||||
RUN-DEFAULT is a function to call when neither debug or scene flag
|
||||
is selected in hydra.
|
||||
RUN-DEBUG is a function to call when debug flag is selected in hydra.
|
||||
RUN-EDITOR is a function to call when editor flag is selected in hydra."
|
||||
(cond
|
||||
((and (not gdscript-hydra--debug)
|
||||
(not gdscript-hydra--editor)) (funcall run-default))
|
||||
(gdscript-hydra--debug (funcall run-debug))
|
||||
(gdscript-hydra--editor (funcall run-editor))))
|
||||
|
||||
(defun gdscript-hydra--run (project-or-scene)
|
||||
"Dispatcher from hydra heads to gdscript-godot-* commands.
|
||||
|
||||
PROJECT-OR-SCENE is symbol representing scene, project or script.
|
||||
It is setting variable `gdscript-godot--debug-options-hydra' based
|
||||
on hydra checkboxes."
|
||||
(setq gdscript-godot--debug-options-hydra
|
||||
(remove nil
|
||||
(list
|
||||
(when gdscript-hydra--debug-collisions "--debug-collisions")
|
||||
(when gdscript-hydra--debug-navigation "--debug-navigation"))))
|
||||
|
||||
(pcase project-or-scene
|
||||
(:project (gdscript-hydra--dispatch 'gdscript-godot-run-project
|
||||
'gdscript-godot-run-project-debug
|
||||
'gdscript-godot-open-project-in-editor))
|
||||
(:scene (gdscript-hydra--dispatch 'gdscript-godot-run-current-scene
|
||||
'gdscript-godot-run-current-scene-debug
|
||||
'gdscript-godot-edit-current-scene))
|
||||
(:script (gdscript-godot-run-current-script))))
|
||||
|
||||
(defun gdscript-hydra--open-godot-buffer ()
|
||||
"Find buffer named *godot - <project-name>* and if it exists open it in other window."
|
||||
(let* ((current-name (buffer-name (current-buffer)))
|
||||
(godot-buffer-name (gdscript-util--get-godot-buffer-name)))
|
||||
(unless (string= godot-buffer-name current-name)
|
||||
(let ((godot-buffer (seq-find
|
||||
(lambda (current-buffer)
|
||||
(with-current-buffer current-buffer
|
||||
(equal (buffer-name) godot-buffer-name))) (buffer-list))))
|
||||
(when godot-buffer (switch-to-buffer-other-window godot-buffer))))))
|
||||
|
||||
(defun gdscript-hydra--run-last ()
|
||||
"Run last command from history."
|
||||
(gdscript-godot--run-command (gdscript-history--last-command)))
|
||||
|
||||
(defun gdscript-hydra--select-from-history ()
|
||||
"Choose command to run from history of commands."
|
||||
(gdscript-godot--run-command (gdscript-history--select-from-history)))
|
||||
|
||||
(ignore-errors
|
||||
;; Don't signal an error when hydra.el is not present
|
||||
(defhydra gdscript-hydra--menu (:hint none
|
||||
:body-pre (setq gdscript-hydra--open t)
|
||||
:before-exit (setq gdscript-hydra--open nil))
|
||||
"
|
||||
_d_ (?d?) Debug _p_ run project _t_ run script _h_ run from history _a_ format all _q_ quit
|
||||
_e_ (?e?) Editor _s_ run scene _r_ run last _g_ switch to *godot* _b_ format buffer
|
||||
|
||||
_c_ [?c?] Visible collisions shapes
|
||||
_n_ [?n?] Visible navigation
|
||||
"
|
||||
("d" (progn
|
||||
(setq gdscript-hydra--debug (not gdscript-hydra--debug)
|
||||
gdscript-hydra--editor nil)
|
||||
(unless gdscript-hydra--debug
|
||||
(setq
|
||||
gdscript-hydra--debug-collisions nil
|
||||
gdscript-hydra--debug-navigation nil))) (gdscript-hydra--selected gdscript-hydra--debug))
|
||||
("e" (setq gdscript-hydra--editor (not gdscript-hydra--editor)
|
||||
gdscript-hydra--debug nil
|
||||
gdscript-hydra--debug-collisions nil
|
||||
gdscript-hydra--debug-navigation nil) (gdscript-hydra--selected gdscript-hydra--editor))
|
||||
("p" (gdscript-hydra--run :project))
|
||||
("s" (gdscript-hydra--run :scene))
|
||||
("t" (gdscript-hydra--run :script))
|
||||
("r" (gdscript-hydra--run-last))
|
||||
("h" (gdscript-hydra--select-from-history))
|
||||
("c" (setq gdscript-hydra--debug-collisions (not gdscript-hydra--debug-collisions)
|
||||
gdscript-hydra--debug t
|
||||
gdscript-hydra--editor nil) (gdscript-hydra--selected gdscript-hydra--debug-collisions))
|
||||
("n" (setq gdscript-hydra--debug-navigation (not gdscript-hydra--debug-navigation)
|
||||
gdscript-hydra--debug t
|
||||
gdscript-hydra--editor nil) (gdscript-hydra--selected gdscript-hydra--debug-navigation))
|
||||
("g" (gdscript-hydra--open-godot-buffer) :color blue)
|
||||
("a" (gdscript-format-all))
|
||||
("b" (gdscript-format-buffer))
|
||||
("q" nil)))
|
||||
|
||||
(provide 'gdscript-hydra)
|
||||
|
||||
;;; gdscript-hydra.el ends here
|
||||
@@ -45,45 +45,6 @@
|
||||
The name of the defun should be grouped so it can be retrieved
|
||||
via `match-string'.")
|
||||
|
||||
;;; Indentation
|
||||
(defun gdscript-indent-guess-indent-offset ()
|
||||
"Guess and set `gdscript-indent-offset' for the current buffer."
|
||||
(interactive)
|
||||
(save-excursion
|
||||
(save-restriction
|
||||
(widen)
|
||||
(goto-char (point-min))
|
||||
(let ((block-end))
|
||||
(while (and (not block-end)
|
||||
(re-search-forward
|
||||
(gdscript-rx line-start block-start) nil t))
|
||||
(when (and
|
||||
(not (gdscript-syntax-context-type))
|
||||
(progn
|
||||
(goto-char (line-end-position))
|
||||
(gdscript--util-forward-comment -1)
|
||||
(if (equal (char-before) ?:)
|
||||
t
|
||||
(forward-line 1)
|
||||
(when (gdscript-info-block-continuation-line-p)
|
||||
(while (and (gdscript-info-continuation-line-p)
|
||||
(not (eobp)))
|
||||
(forward-line 1))
|
||||
(gdscript--util-forward-comment -1)
|
||||
(when (equal (char-before) ?:)
|
||||
t)))))
|
||||
(setq block-end (point-marker))))
|
||||
(let ((indentation
|
||||
(when block-end
|
||||
(goto-char block-end)
|
||||
(gdscript--util-forward-comment)
|
||||
(current-indentation))))
|
||||
(if (and indentation (not (zerop indentation)))
|
||||
(set (make-local-variable 'gdscript-indent-offset) indentation)
|
||||
(when gdscript-indent-guess-indent-offset-verbose
|
||||
(message "Can't guess gdscript-indent-offset, using defaults: %s"
|
||||
gdscript-indent-offset))))))))
|
||||
|
||||
(defun gdscript-indent-context ()
|
||||
"Get information about the current indentation context.
|
||||
Context is returned in a cons with the form (STATUS . START).
|
||||
@@ -697,6 +658,19 @@ likely an invalid gdscript file."
|
||||
(setq positions (cdr positions)))))
|
||||
position))
|
||||
|
||||
(defun gdscript-indent--nav-block ()
|
||||
"Search backward for block start."
|
||||
(re-search-backward (gdscript-rx block-start) nil t)
|
||||
;; At this point `(match-string-no-properties 0)' doesn't match whole gdscript's block expression,
|
||||
;; due to how `re-search-backward' works.
|
||||
;; To make `(match-string-no-properties 0)' match whole gdscript's block expression
|
||||
;; let's use `re-search-forward' from beginning of a statement.
|
||||
(gdscript-nav-beginning-of-statement)
|
||||
(prog1
|
||||
(re-search-forward (gdscript-rx block-start) nil t)
|
||||
;; And let's put point at the beginning of the statement.
|
||||
(gdscript-nav-beginning-of-statement)))
|
||||
|
||||
(defun gdscript-info-dedenter-opening-block-positions ()
|
||||
"Return points of blocks the current line may close sorted by closer.
|
||||
Returns nil if point is not on a dedenter statement or no opening
|
||||
@@ -708,18 +682,13 @@ likely an invalid gdscript file."
|
||||
(goto-char dedenter-pos)
|
||||
(let* ((cur-line (line-beginning-position))
|
||||
(pairs '(("elif" "elif" "if")
|
||||
("else" "if" "elif" "except" "for" "while")
|
||||
("except" "except" "try")
|
||||
("finally" "else" "except" "try")))
|
||||
("else" "if" "elif")))
|
||||
(dedenter (match-string-no-properties 0))
|
||||
(possible-opening-blocks (cdr (assoc-string dedenter pairs)))
|
||||
(collected-indentations)
|
||||
(opening-blocks))
|
||||
(catch 'exit
|
||||
(while (gdscript-nav--syntactically
|
||||
(lambda ()
|
||||
(re-search-backward (gdscript-rx block-start) nil t))
|
||||
#'<)
|
||||
(while (gdscript-nav--syntactically #'gdscript-indent--nav-block #'<)
|
||||
(let ((indentation (current-indentation)))
|
||||
(when (and (not (memq indentation collected-indentations))
|
||||
(or (not collected-indentations)
|
||||
@@ -736,13 +705,17 @@ likely an invalid gdscript file."
|
||||
(while (and (< (point) cur-line)
|
||||
(setq no-back-indent
|
||||
(or (> (current-indentation) indentation)
|
||||
(gdscript-info-current-line-empty-p))))
|
||||
(gdscript-info-current-line-empty-p)
|
||||
(gdscript-info-current-line-comment-p)
|
||||
(not (equal :after-line (car (gdscript-indent-context)))))))
|
||||
(forward-line)))
|
||||
no-back-indent)))
|
||||
(setq collected-indentations
|
||||
(cons indentation collected-indentations))
|
||||
(when (member (match-string-no-properties 0)
|
||||
possible-opening-blocks)
|
||||
(when
|
||||
(seq-contains possible-opening-blocks
|
||||
(string-trim (match-string-no-properties 0))
|
||||
(lambda (elt e) (string-prefix-p e elt)))
|
||||
(setq opening-blocks (cons (point) opening-blocks))))
|
||||
(when (zerop indentation)
|
||||
(throw 'exit nil)))))
|
||||
@@ -882,7 +855,7 @@ operator."
|
||||
(defun gdscript-info-current-line-comment-p ()
|
||||
"Return non-nil if current line is a comment line."
|
||||
(char-equal
|
||||
(or (char-after (+ (line-beginning-position) (current-indentation))) ?_)
|
||||
(or (char-after (+ (line-beginning-position) (/ (current-indentation) gdscript-indent-offset))) ?_)
|
||||
?#))
|
||||
|
||||
(defun gdscript-info-current-line-empty-p ()
|
||||
|
||||
@@ -32,133 +32,136 @@
|
||||
;;; Code:
|
||||
|
||||
(defconst gdscript-keywords '("and" "as" "assert" "break" "breakpoint" "case" "class" "class_name"
|
||||
"const" "continue" "do" "elif" "else" "enum" "export" "extends" "false" "for" "func" "if" "in" "is"
|
||||
"master" "match" "not" "onready" "or" "pass" "preload" "remote" "return" "self" "setget" "signal"
|
||||
"slave" "static" "switch" "sync" "tool" "true" "var" "while" "yield"))
|
||||
"const" "continue" "do" "elif" "else" "enum" "export" "extends" "false" "for" "func" "if" "in" "is"
|
||||
"master" "match" "not" "onready" "or" "pass" "preload" "puppet" "remote" "remotesync" "return" "self" "setget" "signal"
|
||||
"slave" "static" "switch" "sync" "tool" "true" "var" "while" "yield"))
|
||||
(defconst gdscript-built-in-constants '("INF" "NAN" "PI" "TAU"))
|
||||
;; Only contains types that are not classes and that the Godot editor highlights
|
||||
;; like built-in keywords
|
||||
(defconst gdscript-built-in-types '("bool" "float" "int" "null" "void"))
|
||||
(defconst gdscript-built-in-functions '("Color8" "abs" "acos" "asin" "assert" "atan" "atan2"
|
||||
"bytes2var" "ceil" "char" "clamp" "convert" "cos" "cosh" "db2linear" "decimals" "dectime" "deg2rad"
|
||||
"dict2inst" "ease" "exp" "floor" "fmod" "fposmod" "funcref" "hash" "inst2dict" "instance_from_id"
|
||||
"is_inf" "is_nan" "lerp" "linear2db" "load" "log" "max" "min" "name" "nearest_po2" "pow" "preload"
|
||||
"print" "print_stack" "printerr" "printraw" "prints" "printt" "rad2deg" "rand_range" "rand_seed"
|
||||
"randf" "randi" "randomize" "range" "round" "seed" "sign" "sin" "sinh" "sqrt" "stepify" "str"
|
||||
"str2var" "tan" "tanh" "type_exists" "typeof" "var2bytes" "var2str" "weakref" "yield"))
|
||||
(defconst gdscript-built-in-functions '("Color8" "ColorN" "abs" "acos" "asin" "assert" "atan" "atan2"
|
||||
"bytes2var" "cartesian2polar" "ceil" "char" "clamp" "convert" "cos" "cosh" "db2linear" "decimals"
|
||||
"dectime" "deg2rad" "dict2inst" "ease" "exp" "floor" "fmod" "fposmod" "funcref" "get_stack" "hash"
|
||||
"inst2dict" "instance_from_id" "inverse_lerp" "is_equal_approx" "is_inf" "is_instance_valid" "is_nan"
|
||||
"is_zero_approx" "len" "lerp" "lerp_angle" "linear2db" "load" "log" "max" "min" "move_toward" "nearest_po2"
|
||||
"ord" "parse_json" "polar2cartesian" "posmod" "pow" "preload" "print" "print_debug" "print_stack" "printerr"
|
||||
"printraw" "prints" "printt" "push_error" "push_warning" "rad2deg" "rand_range" "rand_seed" "randf" "randi"
|
||||
"randomize" "range" "range_lerp" "round" "seed" "sign" "sin" "sinh" "smoothstep" "sqrt" "step_decimals" "stepify"
|
||||
"str" "str2var" "tan" "tanh" "to_json" "type_exists" "typeof" "validate_json" "var2bytes" "var2str" "weakref"
|
||||
"wrapf" "wrapi" "yield"))
|
||||
;; Contains all engine classes and node types, including vectors, transforms, etc.
|
||||
(defconst gdscript-built-in-classes '("AABB" "ARVRAnchor" "ARVRCamera" "ARVRController"
|
||||
"ARVRInterface" "ARVROrigin" "ARVRPositionalTracker" "ARVRServer" "AStar" "AStar2D" "AcceptDialog"
|
||||
"AnimatedSprite" "AnimatedSprite3D" "AnimatedTexture" "Animation" "AnimationNode"
|
||||
"AnimationNodeAdd2" "AnimationNodeAdd3" "AnimationNodeAnimation" "AnimationNodeBlend2"
|
||||
"AnimationNodeBlend3" "AnimationNodeBlendSpace1D" "AnimationNodeBlendSpace2D"
|
||||
"AnimationNodeBlendTree" "AnimationNodeOneShot" "AnimationNodeOutput" "AnimationNodeStateMachine"
|
||||
"AnimationNodeStateMachinePlayback" "AnimationNodeStateMachineTransition" "AnimationNodeTimeScale"
|
||||
"AnimationNodeTimeSeek" "AnimationNodeTransition" "AnimationPlayer" "AnimationRootNode"
|
||||
"AnimationTrackEditPlugin" "AnimationTree" "AnimationTreePlayer" "Area" "Area2D" "Array"
|
||||
"ArrayMesh" "AtlasTexture" "AudioBusLayout" "AudioEffect" "AudioEffectAmplify"
|
||||
"AudioEffectBandLimitFilter" "AudioEffectBandPassFilter" "AudioEffectChorus"
|
||||
"AudioEffectCompressor" "AudioEffectDelay" "AudioEffectDistortion" "AudioEffectEQ"
|
||||
"AudioEffectEQ10" "AudioEffectEQ21" "AudioEffectEQ6" "AudioEffectFilter"
|
||||
"AudioEffectHighPassFilter" "AudioEffectHighShelfFilter" "AudioEffectInstance"
|
||||
"AudioEffectLimiter" "AudioEffectLowPassFilter" "AudioEffectLowShelfFilter"
|
||||
"AudioEffectNotchFilter" "AudioEffectPanner" "AudioEffectPhaser" "AudioEffectPitchShift"
|
||||
"AudioEffectRecord" "AudioEffectReverb" "AudioEffectSpectrumAnalyzer"
|
||||
"AudioEffectSpectrumAnalyzerInstance" "AudioEffectStereoEnhance" "AudioServer" "AudioStream"
|
||||
"AudioStreamGenerator" "AudioStreamGeneratorPlayback" "AudioStreamMicrophone"
|
||||
"AudioStreamPlayback" "AudioStreamPlaybackResampled" "AudioStreamPlayer" "AudioStreamPlayer2D"
|
||||
"AudioStreamPlayer3D" "AudioStreamRandomPitch" "AudioStreamSample" "BackBufferCopy"
|
||||
"BakedLightmap" "BakedLightmapData" "BaseButton" "Basis" "BitMap" "BitmapFont" "Bone2D"
|
||||
"BoneAttachment" "BoxContainer" "BoxShape" "Button" "ButtonGroup" "CPUParticles" "CPUParticles2D"
|
||||
"Camera" "Camera2D" "CameraFeed" "CameraServer" "CameraTexture" "CanvasItem" "CanvasItemMaterial"
|
||||
"CanvasLayer" "CanvasModulate" "CapsuleMesh" "CapsuleShape" "CapsuleShape2D" "CenterContainer"
|
||||
"CharFXTransform" "CheckBox" "CheckButton" "CircleShape2D" "ClassDB" "ClippedCamera"
|
||||
"CollisionObject" "CollisionObject2D" "CollisionPolygon" "CollisionPolygon2D" "CollisionShape"
|
||||
"CollisionShape2D" "Color" "ColorPicker" "ColorPickerButton" "ColorRect" "ConcavePolygonShape"
|
||||
"ConcavePolygonShape2D" "ConeTwistJoint" "ConfigFile" "ConfirmationDialog" "Container" "Control"
|
||||
"ConvexPolygonShape" "ConvexPolygonShape2D" "Crypto" "CryptoKey" "CubeMap" "CubeMesh" "Curve"
|
||||
"Curve2D" "Curve3D" "CurveTexture" "CylinderMesh" "CylinderShape" "DampedSpringJoint2D"
|
||||
"Dictionary" "DirectionalLight" "Directory" "DynamicFont" "DynamicFontData" "EditorExportPlugin"
|
||||
"EditorFeatureProfile" "EditorFileDialog" "EditorFileSystem" "EditorFileSystemDirectory"
|
||||
"EditorImportPlugin" "EditorInspector" "EditorInspectorPlugin" "EditorInterface"
|
||||
"EditorNavigationMeshGenerator" "EditorPlugin" "EditorProperty" "EditorResourceConversionPlugin"
|
||||
"EditorResourcePreview" "EditorResourcePreviewGenerator" "EditorSceneImporter"
|
||||
"EditorSceneImporterAssimp" "EditorScenePostImport" "EditorScript" "EditorSelection"
|
||||
"EditorSettings" "EditorSpatialGizmo" "EditorSpatialGizmoPlugin" "EditorSpinSlider"
|
||||
"EditorVCSInterface" "EncodedObjectAsID" "Engine" "Environment" "Expression" "File" "FileDialog"
|
||||
"Font" "FuncRef" "GIProbe" "GIProbeData" "Generic6DOFJoint" "Geometry" "GeometryInstance"
|
||||
"Gradient" "GradientTexture" "GraphEdit" "GraphNode" "GridContainer" "GrooveJoint2D"
|
||||
"HBoxContainer" "HScrollBar" "HSeparator" "HSlider" "HSplitContainer" "HTTPClient" "HTTPRequest"
|
||||
"HashingContext" "HeightMapShape" "HingeJoint" "IP" "IP_Unix" "Image" "ImageTexture"
|
||||
"ImmediateGeometry" "Input" "InputDefault" "InputEvent" "InputEventAction" "InputEventGesture"
|
||||
"InputEventJoypadButton" "InputEventJoypadMotion" "InputEventKey" "InputEventMIDI"
|
||||
"InputEventMagnifyGesture" "InputEventMouse" "InputEventMouseButton" "InputEventMouseMotion"
|
||||
"InputEventPanGesture" "InputEventScreenDrag" "InputEventScreenTouch" "InputEventWithModifiers"
|
||||
"InputMap" "InstancePlaceholder" "InterpolatedCamera" "ItemList" "JSON" "JSONParseResult"
|
||||
"JSONRPC" "JavaScript" "Joint" "Joint2D" "KinematicBody" "KinematicBody2D" "KinematicCollision"
|
||||
"KinematicCollision2D" "Label" "LargeTexture" "Light" "Light2D" "LightOccluder2D" "Line2D"
|
||||
"LineEdit" "LineShape2D" "LinkButton" "Listener" "MainLoop" "MarginContainer" "Marshalls"
|
||||
"Material" "MenuButton" "Mesh" "MeshDataTool" "MeshInstance" "MeshInstance2D" "MeshLibrary"
|
||||
"MeshTexture" "MultiMesh" "MultiMeshInstance" "MultiMeshInstance2D" "MultiplayerAPI" "Mutex"
|
||||
"Navigation" "Navigation2D" "NavigationMesh" "NavigationMeshInstance" "NavigationPolygon"
|
||||
"NavigationPolygonInstance" "NetworkedMultiplayerPeer" "Nil" "NinePatchRect" "Node" "Node2D"
|
||||
"NodePath" "OS" "Object" "OccluderPolygon2D" "OmniLight" "OptionButton" "PCKPacker"
|
||||
"PHashTranslation" "PackedDataContainer" "PackedDataContainerRef" "PackedScene" "PacketPeer"
|
||||
"PacketPeerStream" "PacketPeerUDP" "Panel" "PanelContainer" "PanoramaSky" "ParallaxBackground"
|
||||
"ParallaxLayer" "Particles" "Particles2D" "ParticlesMaterial" "Path" "Path2D" "PathFollow"
|
||||
"PathFollow2D" "Performance" "PhysicalBone" "Physics2DDirectBodyState"
|
||||
"Physics2DDirectBodyStateSW" "Physics2DDirectSpaceState" "Physics2DServer" "Physics2DServerSW"
|
||||
"Physics2DShapeQueryParameters" "Physics2DShapeQueryResult" "Physics2DTestMotionResult"
|
||||
"PhysicsBody" "PhysicsBody2D" "PhysicsDirectBodyState" "PhysicsDirectSpaceState" "PhysicsMaterial"
|
||||
"PhysicsServer" "PhysicsShapeQueryParameters" "PhysicsShapeQueryResult" "PinJoint" "PinJoint2D"
|
||||
"Plane" "PlaneMesh" "PlaneShape" "PointMesh" "Polygon2D" "PolygonPathFinder" "PoolByteArray"
|
||||
"PoolColorArray" "PoolIntArray" "PoolRealArray" "PoolStringArray" "PoolVector2Array"
|
||||
"PoolVector3Array" "Popup" "PopupDialog" "PopupMenu" "PopupPanel" "Position2D" "Position3D"
|
||||
"PrimitiveMesh" "PrismMesh" "ProceduralSky" "ProgressBar" "ProjectSettings" "ProximityGroup"
|
||||
"ProxyTexture" "QuadMesh" "Quat" "RID" "RandomNumberGenerator" "Range" "RayCast" "RayCast2D"
|
||||
"RayShape" "RayShape2D" "Rect2" "RectangleShape2D" "Reference" "ReferenceRect" "ReflectionProbe"
|
||||
"RemoteTransform" "RemoteTransform2D" "Resource" "ResourceFormatLoader"
|
||||
"ResourceFormatLoaderCrypto" "ResourceFormatSaver" "ResourceFormatSaverCrypto" "ResourceImporter"
|
||||
"ResourceInteractiveLoader" "ResourceLoader" "ResourcePreloader" "ResourceSaver" "RichTextEffect"
|
||||
"RichTextLabel" "RigidBody" "RigidBody2D" "RootMotionView" "SceneState" "SceneTree"
|
||||
"SceneTreeTimer" "Script" "ScriptCreateDialog" "ScriptEditor" "ScrollBar" "ScrollContainer"
|
||||
"SegmentShape2D" "Semaphore" "Separator" "Shader" "ShaderMaterial" "Shape" "Shape2D" "ShortCut"
|
||||
"Skeleton" "Skeleton2D" "SkeletonIK" "Skin" "SkinReference" "Sky" "Slider" "SliderJoint"
|
||||
"SoftBody" "Spatial" "SpatialGizmo" "SpatialMaterial" "SpatialVelocityTracker" "SphereMesh"
|
||||
"SphereShape" "SpinBox" "SplitContainer" "SpotLight" "SpringArm" "Sprite" "Sprite3D"
|
||||
"SpriteBase3D" "SpriteFrames" "StaticBody" "StaticBody2D" "StreamPeer" "StreamPeerBuffer"
|
||||
"StreamPeerSSL" "StreamPeerTCP" "StreamTexture" "String" "StyleBox" "StyleBoxEmpty" "StyleBoxFlat"
|
||||
"StyleBoxLine" "StyleBoxTexture" "SurfaceTool" "TCP_Server" "TabContainer" "Tabs" "TextEdit"
|
||||
"TextFile" "Texture" "Texture3D" "TextureArray" "TextureButton" "TextureLayered" "TextureProgress"
|
||||
"TextureRect" "Theme" "Thread" "TileMap" "TileSet" "Timer" "ToolButton" "TouchScreenButton"
|
||||
"Transform" "Transform2D" "Translation" "TranslationServer" "Tree" "TreeItem" "TriangleMesh"
|
||||
"Tween" "UndoRedo" "VBoxContainer" "VScrollBar" "VSeparator" "VSlider" "VSplitContainer" "Variant"
|
||||
"Vector2" "Vector3" "VehicleBody" "VehicleWheel" "VideoPlayer" "VideoStream" "Viewport"
|
||||
"ViewportContainer" "ViewportTexture" "VisibilityEnabler" "VisibilityEnabler2D"
|
||||
"VisibilityNotifier" "VisibilityNotifier2D" "VisualInstance" "VisualServer" "VisualShader"
|
||||
"VisualShaderNode" "VisualShaderNodeBooleanConstant" "VisualShaderNodeBooleanUniform"
|
||||
"VisualShaderNodeColorConstant" "VisualShaderNodeColorFunc" "VisualShaderNodeColorOp"
|
||||
"VisualShaderNodeColorUniform" "VisualShaderNodeCompare" "VisualShaderNodeCubeMap"
|
||||
"VisualShaderNodeCubeMapUniform" "VisualShaderNodeCustom" "VisualShaderNodeDeterminant"
|
||||
"VisualShaderNodeDotProduct" "VisualShaderNodeExpression" "VisualShaderNodeFaceForward"
|
||||
"VisualShaderNodeFresnel" "VisualShaderNodeGlobalExpression" "VisualShaderNodeGroupBase"
|
||||
"VisualShaderNodeIf" "VisualShaderNodeInput" "VisualShaderNodeIs" "VisualShaderNodeOuterProduct"
|
||||
"VisualShaderNodeOutput" "VisualShaderNodeScalarClamp" "VisualShaderNodeScalarConstant"
|
||||
"VisualShaderNodeScalarDerivativeFunc" "VisualShaderNodeScalarFunc" "VisualShaderNodeScalarInterp"
|
||||
"VisualShaderNodeScalarOp" "VisualShaderNodeScalarSmoothStep" "VisualShaderNodeScalarSwitch"
|
||||
"VisualShaderNodeScalarUniform" "VisualShaderNodeSwitch" "VisualShaderNodeTexture"
|
||||
"VisualShaderNodeTextureUniform" "VisualShaderNodeTextureUniformTriplanar"
|
||||
"VisualShaderNodeTransformCompose" "VisualShaderNodeTransformConstant"
|
||||
"VisualShaderNodeTransformDecompose" "VisualShaderNodeTransformFunc"
|
||||
"VisualShaderNodeTransformMult" "VisualShaderNodeTransformUniform"
|
||||
"VisualShaderNodeTransformVecMult" "VisualShaderNodeUniform" "VisualShaderNodeVec3Constant"
|
||||
"VisualShaderNodeVec3Uniform" "VisualShaderNodeVectorClamp" "VisualShaderNodeVectorCompose"
|
||||
"VisualShaderNodeVectorDecompose" "VisualShaderNodeVectorDerivativeFunc"
|
||||
"VisualShaderNodeVectorDistance" "VisualShaderNodeVectorFunc" "VisualShaderNodeVectorInterp"
|
||||
"VisualShaderNodeVectorLen" "VisualShaderNodeVectorOp" "VisualShaderNodeVectorRefract"
|
||||
"VisualShaderNodeVectorScalarMix" "VisualShaderNodeVectorScalarSmoothStep"
|
||||
"VisualShaderNodeVectorScalarStep" "VisualShaderNodeVectorSmoothStep" "WeakRef" "WindowDialog"
|
||||
"World" "World2D" "WorldEnvironment" "X509Certificate" "XMLParser" "YSort"))
|
||||
"ARVRInterface" "ARVROrigin" "ARVRPositionalTracker" "ARVRServer" "AStar" "AStar2D" "AcceptDialog"
|
||||
"AnimatedSprite" "AnimatedSprite3D" "AnimatedTexture" "Animation" "AnimationNode"
|
||||
"AnimationNodeAdd2" "AnimationNodeAdd3" "AnimationNodeAnimation" "AnimationNodeBlend2"
|
||||
"AnimationNodeBlend3" "AnimationNodeBlendSpace1D" "AnimationNodeBlendSpace2D"
|
||||
"AnimationNodeBlendTree" "AnimationNodeOneShot" "AnimationNodeOutput" "AnimationNodeStateMachine"
|
||||
"AnimationNodeStateMachinePlayback" "AnimationNodeStateMachineTransition" "AnimationNodeTimeScale"
|
||||
"AnimationNodeTimeSeek" "AnimationNodeTransition" "AnimationPlayer" "AnimationRootNode"
|
||||
"AnimationTrackEditPlugin" "AnimationTree" "AnimationTreePlayer" "Area" "Area2D" "Array"
|
||||
"ArrayMesh" "AtlasTexture" "AudioBusLayout" "AudioEffect" "AudioEffectAmplify"
|
||||
"AudioEffectBandLimitFilter" "AudioEffectBandPassFilter" "AudioEffectChorus"
|
||||
"AudioEffectCompressor" "AudioEffectDelay" "AudioEffectDistortion" "AudioEffectEQ"
|
||||
"AudioEffectEQ10" "AudioEffectEQ21" "AudioEffectEQ6" "AudioEffectFilter"
|
||||
"AudioEffectHighPassFilter" "AudioEffectHighShelfFilter" "AudioEffectInstance"
|
||||
"AudioEffectLimiter" "AudioEffectLowPassFilter" "AudioEffectLowShelfFilter"
|
||||
"AudioEffectNotchFilter" "AudioEffectPanner" "AudioEffectPhaser" "AudioEffectPitchShift"
|
||||
"AudioEffectRecord" "AudioEffectReverb" "AudioEffectSpectrumAnalyzer"
|
||||
"AudioEffectSpectrumAnalyzerInstance" "AudioEffectStereoEnhance" "AudioServer" "AudioStream"
|
||||
"AudioStreamGenerator" "AudioStreamGeneratorPlayback" "AudioStreamMicrophone"
|
||||
"AudioStreamPlayback" "AudioStreamPlaybackResampled" "AudioStreamPlayer" "AudioStreamPlayer2D"
|
||||
"AudioStreamPlayer3D" "AudioStreamRandomPitch" "AudioStreamSample" "BackBufferCopy"
|
||||
"BakedLightmap" "BakedLightmapData" "BaseButton" "Basis" "BitMap" "BitmapFont" "Bone2D"
|
||||
"BoneAttachment" "BoxContainer" "BoxShape" "Button" "ButtonGroup" "CPUParticles" "CPUParticles2D"
|
||||
"Camera" "Camera2D" "CameraFeed" "CameraServer" "CameraTexture" "CanvasItem" "CanvasItemMaterial"
|
||||
"CanvasLayer" "CanvasModulate" "CapsuleMesh" "CapsuleShape" "CapsuleShape2D" "CenterContainer"
|
||||
"CharFXTransform" "CheckBox" "CheckButton" "CircleShape2D" "ClassDB" "ClippedCamera"
|
||||
"CollisionObject" "CollisionObject2D" "CollisionPolygon" "CollisionPolygon2D" "CollisionShape"
|
||||
"CollisionShape2D" "Color" "ColorPicker" "ColorPickerButton" "ColorRect" "ConcavePolygonShape"
|
||||
"ConcavePolygonShape2D" "ConeTwistJoint" "ConfigFile" "ConfirmationDialog" "Container" "Control"
|
||||
"ConvexPolygonShape" "ConvexPolygonShape2D" "Crypto" "CryptoKey" "CubeMap" "CubeMesh" "Curve"
|
||||
"Curve2D" "Curve3D" "CurveTexture" "CylinderMesh" "CylinderShape" "DampedSpringJoint2D"
|
||||
"Dictionary" "DirectionalLight" "Directory" "DynamicFont" "DynamicFontData" "EditorExportPlugin"
|
||||
"EditorFeatureProfile" "EditorFileDialog" "EditorFileSystem" "EditorFileSystemDirectory"
|
||||
"EditorImportPlugin" "EditorInspector" "EditorInspectorPlugin" "EditorInterface"
|
||||
"EditorNavigationMeshGenerator" "EditorPlugin" "EditorProperty" "EditorResourceConversionPlugin"
|
||||
"EditorResourcePreview" "EditorResourcePreviewGenerator" "EditorSceneImporter"
|
||||
"EditorSceneImporterAssimp" "EditorScenePostImport" "EditorScript" "EditorSelection"
|
||||
"EditorSettings" "EditorSpatialGizmo" "EditorSpatialGizmoPlugin" "EditorSpinSlider"
|
||||
"EditorVCSInterface" "EncodedObjectAsID" "Engine" "Environment" "Expression" "File" "FileDialog"
|
||||
"Font" "FuncRef" "GIProbe" "GIProbeData" "Generic6DOFJoint" "Geometry" "GeometryInstance"
|
||||
"Gradient" "GradientTexture" "GraphEdit" "GraphNode" "GridContainer" "GrooveJoint2D"
|
||||
"HBoxContainer" "HScrollBar" "HSeparator" "HSlider" "HSplitContainer" "HTTPClient" "HTTPRequest"
|
||||
"HashingContext" "HeightMapShape" "HingeJoint" "IP" "IP_Unix" "Image" "ImageTexture"
|
||||
"ImmediateGeometry" "Input" "InputDefault" "InputEvent" "InputEventAction" "InputEventGesture"
|
||||
"InputEventJoypadButton" "InputEventJoypadMotion" "InputEventKey" "InputEventMIDI"
|
||||
"InputEventMagnifyGesture" "InputEventMouse" "InputEventMouseButton" "InputEventMouseMotion"
|
||||
"InputEventPanGesture" "InputEventScreenDrag" "InputEventScreenTouch" "InputEventWithModifiers"
|
||||
"InputMap" "InstancePlaceholder" "InterpolatedCamera" "ItemList" "JSON" "JSONParseResult"
|
||||
"JSONRPC" "JavaScript" "Joint" "Joint2D" "KinematicBody" "KinematicBody2D" "KinematicCollision"
|
||||
"KinematicCollision2D" "Label" "LargeTexture" "Light" "Light2D" "LightOccluder2D" "Line2D"
|
||||
"LineEdit" "LineShape2D" "LinkButton" "Listener" "MainLoop" "MarginContainer" "Marshalls"
|
||||
"Material" "MenuButton" "Mesh" "MeshDataTool" "MeshInstance" "MeshInstance2D" "MeshLibrary"
|
||||
"MeshTexture" "MultiMesh" "MultiMeshInstance" "MultiMeshInstance2D" "MultiplayerAPI" "Mutex"
|
||||
"Navigation" "Navigation2D" "NavigationMesh" "NavigationMeshInstance" "NavigationPolygon"
|
||||
"NavigationPolygonInstance" "NetworkedMultiplayerPeer" "Nil" "NinePatchRect" "Node" "Node2D"
|
||||
"NodePath" "OS" "Object" "OccluderPolygon2D" "OmniLight" "OptionButton" "PCKPacker"
|
||||
"PHashTranslation" "PackedDataContainer" "PackedDataContainerRef" "PackedScene" "PacketPeer"
|
||||
"PacketPeerStream" "PacketPeerUDP" "Panel" "PanelContainer" "PanoramaSky" "ParallaxBackground"
|
||||
"ParallaxLayer" "Particles" "Particles2D" "ParticlesMaterial" "Path" "Path2D" "PathFollow"
|
||||
"PathFollow2D" "Performance" "PhysicalBone" "Physics2DDirectBodyState"
|
||||
"Physics2DDirectBodyStateSW" "Physics2DDirectSpaceState" "Physics2DServer" "Physics2DServerSW"
|
||||
"Physics2DShapeQueryParameters" "Physics2DShapeQueryResult" "Physics2DTestMotionResult"
|
||||
"PhysicsBody" "PhysicsBody2D" "PhysicsDirectBodyState" "PhysicsDirectSpaceState" "PhysicsMaterial"
|
||||
"PhysicsServer" "PhysicsShapeQueryParameters" "PhysicsShapeQueryResult" "PinJoint" "PinJoint2D"
|
||||
"Plane" "PlaneMesh" "PlaneShape" "PointMesh" "Polygon2D" "PolygonPathFinder" "PoolByteArray"
|
||||
"PoolColorArray" "PoolIntArray" "PoolRealArray" "PoolStringArray" "PoolVector2Array"
|
||||
"PoolVector3Array" "Popup" "PopupDialog" "PopupMenu" "PopupPanel" "Position2D" "Position3D"
|
||||
"PrimitiveMesh" "PrismMesh" "ProceduralSky" "ProgressBar" "ProjectSettings" "ProximityGroup"
|
||||
"ProxyTexture" "QuadMesh" "Quat" "RID" "RandomNumberGenerator" "Range" "RayCast" "RayCast2D"
|
||||
"RayShape" "RayShape2D" "Rect2" "RectangleShape2D" "Reference" "ReferenceRect" "ReflectionProbe"
|
||||
"RemoteTransform" "RemoteTransform2D" "Resource" "ResourceFormatLoader"
|
||||
"ResourceFormatLoaderCrypto" "ResourceFormatSaver" "ResourceFormatSaverCrypto" "ResourceImporter"
|
||||
"ResourceInteractiveLoader" "ResourceLoader" "ResourcePreloader" "ResourceSaver" "RichTextEffect"
|
||||
"RichTextLabel" "RigidBody" "RigidBody2D" "RootMotionView" "SceneState" "SceneTree"
|
||||
"SceneTreeTimer" "Script" "ScriptCreateDialog" "ScriptEditor" "ScrollBar" "ScrollContainer"
|
||||
"SegmentShape2D" "Semaphore" "Separator" "Shader" "ShaderMaterial" "Shape" "Shape2D" "ShortCut"
|
||||
"Skeleton" "Skeleton2D" "SkeletonIK" "Skin" "SkinReference" "Sky" "Slider" "SliderJoint"
|
||||
"SoftBody" "Spatial" "SpatialGizmo" "SpatialMaterial" "SpatialVelocityTracker" "SphereMesh"
|
||||
"SphereShape" "SpinBox" "SplitContainer" "SpotLight" "SpringArm" "Sprite" "Sprite3D"
|
||||
"SpriteBase3D" "SpriteFrames" "StaticBody" "StaticBody2D" "StreamPeer" "StreamPeerBuffer"
|
||||
"StreamPeerSSL" "StreamPeerTCP" "StreamTexture" "String" "StyleBox" "StyleBoxEmpty" "StyleBoxFlat"
|
||||
"StyleBoxLine" "StyleBoxTexture" "SurfaceTool" "TCP_Server" "TabContainer" "Tabs" "TextEdit"
|
||||
"TextFile" "Texture" "Texture3D" "TextureArray" "TextureButton" "TextureLayered" "TextureProgress"
|
||||
"TextureRect" "Theme" "Thread" "TileMap" "TileSet" "Timer" "ToolButton" "TouchScreenButton"
|
||||
"Transform" "Transform2D" "Translation" "TranslationServer" "Tree" "TreeItem" "TriangleMesh"
|
||||
"Tween" "UndoRedo" "VBoxContainer" "VScrollBar" "VSeparator" "VSlider" "VSplitContainer" "Variant"
|
||||
"Vector2" "Vector3" "VehicleBody" "VehicleWheel" "VideoPlayer" "VideoStream" "Viewport"
|
||||
"ViewportContainer" "ViewportTexture" "VisibilityEnabler" "VisibilityEnabler2D"
|
||||
"VisibilityNotifier" "VisibilityNotifier2D" "VisualInstance" "VisualServer" "VisualShader"
|
||||
"VisualShaderNode" "VisualShaderNodeBooleanConstant" "VisualShaderNodeBooleanUniform"
|
||||
"VisualShaderNodeColorConstant" "VisualShaderNodeColorFunc" "VisualShaderNodeColorOp"
|
||||
"VisualShaderNodeColorUniform" "VisualShaderNodeCompare" "VisualShaderNodeCubeMap"
|
||||
"VisualShaderNodeCubeMapUniform" "VisualShaderNodeCustom" "VisualShaderNodeDeterminant"
|
||||
"VisualShaderNodeDotProduct" "VisualShaderNodeExpression" "VisualShaderNodeFaceForward"
|
||||
"VisualShaderNodeFresnel" "VisualShaderNodeGlobalExpression" "VisualShaderNodeGroupBase"
|
||||
"VisualShaderNodeIf" "VisualShaderNodeInput" "VisualShaderNodeIs" "VisualShaderNodeOuterProduct"
|
||||
"VisualShaderNodeOutput" "VisualShaderNodeScalarClamp" "VisualShaderNodeScalarConstant"
|
||||
"VisualShaderNodeScalarDerivativeFunc" "VisualShaderNodeScalarFunc" "VisualShaderNodeScalarInterp"
|
||||
"VisualShaderNodeScalarOp" "VisualShaderNodeScalarSmoothStep" "VisualShaderNodeScalarSwitch"
|
||||
"VisualShaderNodeScalarUniform" "VisualShaderNodeSwitch" "VisualShaderNodeTexture"
|
||||
"VisualShaderNodeTextureUniform" "VisualShaderNodeTextureUniformTriplanar"
|
||||
"VisualShaderNodeTransformCompose" "VisualShaderNodeTransformConstant"
|
||||
"VisualShaderNodeTransformDecompose" "VisualShaderNodeTransformFunc"
|
||||
"VisualShaderNodeTransformMult" "VisualShaderNodeTransformUniform"
|
||||
"VisualShaderNodeTransformVecMult" "VisualShaderNodeUniform" "VisualShaderNodeVec3Constant"
|
||||
"VisualShaderNodeVec3Uniform" "VisualShaderNodeVectorClamp" "VisualShaderNodeVectorCompose"
|
||||
"VisualShaderNodeVectorDecompose" "VisualShaderNodeVectorDerivativeFunc"
|
||||
"VisualShaderNodeVectorDistance" "VisualShaderNodeVectorFunc" "VisualShaderNodeVectorInterp"
|
||||
"VisualShaderNodeVectorLen" "VisualShaderNodeVectorOp" "VisualShaderNodeVectorRefract"
|
||||
"VisualShaderNodeVectorScalarMix" "VisualShaderNodeVectorScalarSmoothStep"
|
||||
"VisualShaderNodeVectorScalarStep" "VisualShaderNodeVectorSmoothStep" "WeakRef" "WindowDialog"
|
||||
"World" "World2D" "WorldEnvironment" "X509Certificate" "XMLParser" "YSort"))
|
||||
|
||||
(provide 'gdscript-keywords)
|
||||
;;; gdscript-keywords.el ends here
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
;;; Code:
|
||||
|
||||
(require 'gdscript-customization)
|
||||
(require 'gdscript-docs)
|
||||
(require 'gdscript-syntax)
|
||||
(require 'gdscript-indent-and-nav)
|
||||
(require 'gdscript-imenu)
|
||||
@@ -38,6 +39,8 @@
|
||||
(require 'gdscript-completion)
|
||||
(require 'gdscript-format)
|
||||
(require 'gdscript-rx)
|
||||
(require 'gdscript-godot)
|
||||
(require 'gdscript-hydra)
|
||||
|
||||
;;;###autoload
|
||||
(add-to-list 'auto-mode-alist '("\\.gd\\'" . gdscript-mode))
|
||||
@@ -52,19 +55,30 @@
|
||||
(define-key map [remap forward-sentence] 'gdscript-nav-forward-block)
|
||||
(define-key map [remap backward-up-list] 'gdscript-nav-backward-up-list)
|
||||
(define-key map [remap mark-defun] 'gdscript-mark-defun)
|
||||
(define-key map "\C-c\C-j" 'imenu)
|
||||
(define-key map (kbd "C-c C-j") 'imenu)
|
||||
;; Indent specific
|
||||
(define-key map "\177" 'gdscript-indent-dedent-line-backspace)
|
||||
(define-key map (kbd "<backtab>") 'gdscript-indent-dedent-line)
|
||||
(define-key map (kbd "\t") 'company-complete)
|
||||
;; Insertion.
|
||||
(define-key map (kbd "C-c i") 'gdscript-completion-insert-file-path-at-point)
|
||||
;; Formatting.
|
||||
(define-key map (kbd "C-c C-f r") 'gdscript-format-region)
|
||||
(define-key map (kbd "C-c C-f b") 'gdscript-format-buffer)
|
||||
;; Run in Godot.
|
||||
(define-key map "\C-c\C-g" 'gdscript--run-godot-editor)
|
||||
(define-key map "\C-c\C-p" 'gdscript--run-project-in-godot)
|
||||
(define-key map "\C-c\C-s" 'gdscript--run-current-scene-in-godot)
|
||||
(define-key map "\C-c\C-e" 'gdscript-godot-edit-current-scene)
|
||||
(define-key map "\C-c\C-r" 'gdscript--run-current-script-in-godot)
|
||||
(define-key map "\C-c\C-dp" 'gdscript--run-project-in-godot-debug-mode)
|
||||
(define-key map "\C-c\C-ds" 'gdscript--run-current-scene-in-godot-debug-mode)
|
||||
(define-key map (kbd "C-c C-r p") 'gdscript-godot-open-project-in-editor)
|
||||
(define-key map (kbd "C-c C-r r") 'gdscript-godot-run-project)
|
||||
(define-key map (kbd "C-c C-r d") 'gdscript-godot-run-project-debug)
|
||||
(define-key map (kbd "C-c C-r s") 'gdscript-godot-run-current-scene)
|
||||
(define-key map (kbd "C-c C-r q") 'gdscript-godot-run-current-scene-debug)
|
||||
(define-key map (kbd "C-c C-r e") 'gdscript-godot-edit-current-scene)
|
||||
(define-key map (kbd "C-c C-r x") 'gdscript-godot-run-current-script)
|
||||
;; Docs.
|
||||
(define-key map (kbd "C-c C-b a") 'gdscript-docs-browse-api)
|
||||
(define-key map (kbd "C-c C-b o") 'gdscript-docs-browse-symbol-at-point)
|
||||
(define-key map (kbd "C-c C-b s") 'gdscript-docs-online-search-api)
|
||||
;; Hydra
|
||||
(define-key map (kbd "C-c r") 'gdscript-hydra-show)
|
||||
map)
|
||||
"Keymap for `gdscript-mode'.")
|
||||
|
||||
@@ -97,9 +111,9 @@ the last command event was a string delimiter."
|
||||
(setq-local tab-width gdscript-tab-width)
|
||||
(setq-local indent-tabs-mode gdscript-use-tab-indents)
|
||||
|
||||
(set-syntax-table gdscript-syntax-table)
|
||||
(modify-syntax-entry ?\# "\<" gdscript-syntax-table)
|
||||
(modify-syntax-entry ?\n ">" gdscript-syntax-table)
|
||||
(set-syntax-table gdscript-mode-syntax-table)
|
||||
(modify-syntax-entry ?\# "\<" gdscript-mode-syntax-table)
|
||||
(modify-syntax-entry ?\n ">" gdscript-mode-syntax-table)
|
||||
|
||||
(setq-local comment-start "# ")
|
||||
(setq-local comment-start-skip "#+\\s-*")
|
||||
@@ -167,10 +181,7 @@ the last command event was a string delimiter."
|
||||
(gdscript-rx (* space) block-start))
|
||||
(setq-local outline-level
|
||||
#'(lambda ()
|
||||
"`outline-level' function for gdscript mode."
|
||||
(1+ (/ (current-indentation) gdscript-indent-offset))))
|
||||
|
||||
(when gdscript-indent-guess-indent-offset
|
||||
"`outline-level' function for gdscript mode."
|
||||
(1+ (/ (current-indentation) gdscript-indent-offset)))))
|
||||
|
||||
(provide 'gdscript-mode)
|
||||
|
||||
114
gdscript-project.el
Normal file
114
gdscript-project.el
Normal file
@@ -0,0 +1,114 @@
|
||||
;;; gdscript-project.el --- Project -*- lexical-binding: t; -*-
|
||||
;;
|
||||
;; Copyright (C) 2020 GDQuest and contributors
|
||||
;;
|
||||
;; Author: Josef Vlach <vlach.josef@gmail.com>
|
||||
;; URL: https://github.com/GDQuest/emacs-gdscript-mode/
|
||||
;; Version: 1.0.0
|
||||
;; Package-Requires: ((emacs "26.3"))
|
||||
;; Maintainer: nathan@gdquest.com
|
||||
;; Created: June 2020
|
||||
;; Keywords: languages
|
||||
;;
|
||||
;; This file is not part of GNU Emacs.
|
||||
;;
|
||||
;; 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:
|
||||
;;
|
||||
;; Find all scenes/scripts functions.
|
||||
;;
|
||||
;;; Code:
|
||||
|
||||
(require 'gdscript-utils)
|
||||
|
||||
(defvar gdscript-project--script-list nil)
|
||||
|
||||
(defun gdscript-project--current-buffer-scene ()
|
||||
"Return the name of current scene.
|
||||
|
||||
If current buffer is not visiting scene file return nil."
|
||||
(when buffer-file-name
|
||||
(let ((scene-name (concat
|
||||
(gdscript-util--find-project-configuration-file)
|
||||
(gdscript-util--get-godot-project-file-path-relative buffer-file-name) ".tscn")))
|
||||
(when (file-exists-p scene-name) scene-name))))
|
||||
|
||||
(defun gdscript-project--select-scene ()
|
||||
"Find all scenes files and let user choose one."
|
||||
(message "selecting scene")
|
||||
(let* ((rl (gdscript-util--find-project-configuration-file))
|
||||
(scene-list (mapcar (lambda (x) (file-relative-name x rl)) (directory-files-recursively rl ".*.tscn" t)))
|
||||
(prompt (format "Select scene to run" (buffer-name))))
|
||||
(gdscript-util--read scene-list prompt)))
|
||||
|
||||
(defun gdscript-project--current-buffer-script ()
|
||||
"Return the name of current script.
|
||||
|
||||
If current buffer is not visiting script file return nil."
|
||||
(when buffer-file-name
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(when (re-search-forward "^extends SceneTree\\|^extends MainLoop" nil t)
|
||||
(file-relative-name buffer-file-name (gdscript-util--find-project-configuration-file))))))
|
||||
|
||||
(defun gdscript-project--select-script ()
|
||||
"Find all script files and let user choose one."
|
||||
(let ((hydra-open gdscript-hydra--open))
|
||||
(when hydra-open (gdscript-hydra--menu/nil))
|
||||
(unwind-protect
|
||||
(let* ((prompt (format "Buffer %s is not script file, select script to run" (buffer-name)))
|
||||
(script-name (gdscript-util--read gdscript-project--script-list prompt)))
|
||||
(gdscript-godot--run-script script-name))
|
||||
(when hydra-open (gdscript-hydra--menu/body)))))
|
||||
|
||||
(defun gdscript-project--ag-cleanup ()
|
||||
"Clean after ag search.
|
||||
|
||||
Try to leave Emacs as it was before ag search was launched."
|
||||
(remove-hook 'ag-search-finished-hook #'gdscript-project--ag-find-next-script)
|
||||
(let* ((ag-buffer (current-buffer))
|
||||
(ag-window (get-buffer-window ag-buffer))
|
||||
(prev-buffer (window-prev-buffers ag-window)))
|
||||
(if prev-buffer (kill-buffer ag-buffer)
|
||||
(delete-window ag-window)
|
||||
(kill-buffer ag-buffer))))
|
||||
|
||||
(defun gdscript-project--ag-find-next-script ()
|
||||
"Find next script file in ag buffer."
|
||||
(let ((pos (next-single-property-change (point) 'compilation-message))
|
||||
(comp-mes (get-text-property (point) 'compilation-message)))
|
||||
(when comp-mes
|
||||
(let ((file-name (caar (compilation--loc->file-struct (compilation--message->loc comp-mes)))))
|
||||
(push file-name gdscript-project--script-list)))
|
||||
(if pos (progn (goto-char pos)
|
||||
(gdscript-project--ag-find-next-script))
|
||||
(gdscript-project--ag-cleanup)
|
||||
(with-current-buffer (window-buffer (selected-window))
|
||||
(gdscript-project--select-script)))))
|
||||
|
||||
(defun gdscript-project--get-all-scripts ()
|
||||
"Find all script files and let user choose one.
|
||||
|
||||
Since detection of script files require inspection of file contents,
|
||||
this use ag for performance."
|
||||
(if (not (featurep 'ag))
|
||||
(error (format "Buffer %s is no script file. To see all available scripts in current project install package 'ag'." (buffer-name)))
|
||||
(ag-project-regexp "^extends SceneTree|^extends MainLoop")
|
||||
(setq gdscript-project--script-list nil)
|
||||
(add-hook 'ag-search-finished-hook #'gdscript-project--ag-find-next-script)))
|
||||
|
||||
(provide 'gdscript-project)
|
||||
|
||||
;;; gdscript-project.el ends here
|
||||
@@ -1445,10 +1445,13 @@ following constructs:
|
||||
(defmacro gdscript-rx (&rest regexps)
|
||||
"Gdscript mode specialized rx macro.
|
||||
This variant of `rx' supports common Gdscript named REGEXPS."
|
||||
`(gdscript-rx-let ((block-start (seq (zero-or-more nonl)
|
||||
":"
|
||||
(or (seq (zero-or-more " ") eol)
|
||||
(seq (zero-or-more " ") "#" (zero-or-more nonl) eol))))
|
||||
`(gdscript-rx-let (
|
||||
(block-start
|
||||
(or (seq (or "if" "elif" "while" "func") (+? (not ":")) ":")
|
||||
(seq (zero-or-more nonl)
|
||||
":"
|
||||
(or (seq (zero-or-more " ") eol)
|
||||
(seq (zero-or-more " ") "#" (zero-or-more nonl) eol)))))
|
||||
(dedenter (seq symbol-start
|
||||
(or "elif" "else")
|
||||
symbol-end))
|
||||
|
||||
@@ -56,7 +56,11 @@
|
||||
(_ form))))
|
||||
|
||||
;; Controls font-face mappings or colors to highlight groups of keywords
|
||||
(defvar gdscript-font-lock `((,(gdscript-syntax-regex-maker gdscript-keywords)
|
||||
(defvar gdscript-font-lock `((,(rx (and "$" (one-or-more (or "/" (one-or-more word)))))
|
||||
(0 font-lock-constant-face))
|
||||
(,(rx (and (group "$") "\"" (zero-or-more nonl) "\""))
|
||||
(1 font-lock-constant-face))
|
||||
(,(gdscript-syntax-regex-maker gdscript-keywords)
|
||||
1
|
||||
font-lock-keyword-face)
|
||||
(,(gdscript-syntax-regex-maker (append gdscript-built-in-constants
|
||||
@@ -75,9 +79,12 @@
|
||||
(or "var" "const")
|
||||
(1+ space)
|
||||
(group (1+ (or word ?_))))
|
||||
(1 font-lock-variable-name-face))))
|
||||
|
||||
(defvar gdscript-syntax-table (make-syntax-table))
|
||||
(1 font-lock-variable-name-face))
|
||||
;; Function call
|
||||
(,(rx (group (1+ (or word ?_)))
|
||||
(0+ space)
|
||||
"(")
|
||||
(1 font-lock-function-name-face))))
|
||||
|
||||
(defun gdscript-syntax-context (type &optional syntax-ppss)
|
||||
"Return non-nil if point is on TYPE using SYNTAX-PPSS.
|
||||
|
||||
341
gdscript-tests.el
Normal file
341
gdscript-tests.el
Normal file
@@ -0,0 +1,341 @@
|
||||
;;; gdscript-tests.el --- tests for gdscript mode -*- lexical-binding: t; -*-
|
||||
;;
|
||||
;; Copyright (C) 2020 GDQuest and contributors
|
||||
;;
|
||||
;; Author: Josef Vlach <vlach.josef@gmail.com>
|
||||
;; URL: https://github.com/GDQuest/emacs-gdscript-mode/
|
||||
;; Version: 1.0.0
|
||||
;; Package-Requires: ((emacs "26.3"))
|
||||
;; Maintainer: nathan@gdquest.com
|
||||
;; Created: June 2020
|
||||
;; Keywords: languages
|
||||
;;
|
||||
;; This file is not part of GNU Emacs.
|
||||
;;
|
||||
;; 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:
|
||||
;;
|
||||
;; To run the tests in terminal:
|
||||
;; > ./makem.sh test
|
||||
;;
|
||||
;;; Code:
|
||||
|
||||
(require 'ert)
|
||||
(require 'gdscript-mode)
|
||||
|
||||
;; NOTE: `gdscript-tests--with-temp-buffer' and `gdscript-tests-look-at' are
|
||||
;; originally from the Python package
|
||||
|
||||
(defmacro gdscript-tests--with-temp-buffer (contents &rest body)
|
||||
"Create a `gdscript-mode' enabled temp buffer with CONTENTS.
|
||||
BODY is code to be executed within the temp buffer. Point is
|
||||
always located at the beginning of buffer."
|
||||
(declare (indent 1) (debug t))
|
||||
`(with-temp-buffer
|
||||
(let ()
|
||||
(gdscript-mode)
|
||||
(insert ,contents)
|
||||
(goto-char (point-min))
|
||||
,@body)))
|
||||
|
||||
(defun gdscript-tests-look-at (string &optional num restore-point)
|
||||
"Move point at beginning of STRING in the current buffer.
|
||||
Optional argument NUM defaults to 1 and is an integer indicating
|
||||
how many occurrences must be found, when positive the search is
|
||||
done forwards, otherwise backwards. When RESTORE-POINT is
|
||||
non-nil the point is not moved but the position found is still
|
||||
returned. When searching forward and point is already looking at
|
||||
STRING, it is skipped so the next STRING occurrence is selected."
|
||||
(let* ((num (or num 1))
|
||||
(starting-point (point))
|
||||
(string (regexp-quote string))
|
||||
(search-fn (if (> num 0) #'re-search-forward #'re-search-backward))
|
||||
(deinc-fn (if (> num 0) #'1- #'1+))
|
||||
(found-point))
|
||||
(prog2
|
||||
(catch 'exit
|
||||
(while (not (= num 0))
|
||||
(when (and (> num 0)
|
||||
(looking-at string))
|
||||
;; Moving forward and already looking at STRING, skip it.
|
||||
(forward-char (length (match-string-no-properties 0))))
|
||||
(and (not (funcall search-fn string nil t))
|
||||
(throw 'exit t))
|
||||
(when (> num 0)
|
||||
;; `re-search-forward' leaves point at the end of the
|
||||
;; occurrence, move back so point is at the beginning
|
||||
;; instead.
|
||||
(forward-char (- (length (match-string-no-properties 0)))))
|
||||
(setq
|
||||
num (funcall deinc-fn num)
|
||||
found-point (point))))
|
||||
found-point
|
||||
(and restore-point (goto-char starting-point)))))
|
||||
|
||||
(defun gdscript-tests-self-insert (char-or-str)
|
||||
"Call `self-insert-command' for chars in CHAR-OR-STR."
|
||||
(let ((chars
|
||||
(cond
|
||||
((characterp char-or-str)
|
||||
(list char-or-str))
|
||||
((stringp char-or-str)
|
||||
(string-to-list char-or-str))
|
||||
((not
|
||||
(cl-remove-if #'characterp char-or-str))
|
||||
char-or-str)
|
||||
(t (error "CHAR-OR-STR must be a char, string, or list of char")))))
|
||||
(mapc
|
||||
(lambda (char)
|
||||
(let ((last-command-event char))
|
||||
(call-interactively 'self-insert-command)))
|
||||
chars)))
|
||||
|
||||
(ert-deftest gdscript-indent--comment-line ()
|
||||
"Test if current line is comment."
|
||||
(gdscript-tests--with-temp-buffer
|
||||
"
|
||||
func f():
|
||||
# aaa
|
||||
bbb
|
||||
"
|
||||
(gdscript-tests-look-at "aaa")
|
||||
(should (gdscript-info-current-line-comment-p))
|
||||
(gdscript-tests-look-at "bbb")
|
||||
(should (not (gdscript-info-current-line-comment-p)))))
|
||||
|
||||
(ert-deftest gdscript-indent--electric-colon-4 ()
|
||||
"Test indentation case where there is one more-indented previous open block."
|
||||
(gdscript-tests--with-temp-buffer
|
||||
"
|
||||
func f():
|
||||
if true or true:
|
||||
a = 5
|
||||
|
||||
if true and true:
|
||||
a = 10
|
||||
|
||||
b = 3
|
||||
|
||||
else
|
||||
"
|
||||
(gdscript-tests-look-at "else")
|
||||
(goto-char (line-end-position))
|
||||
(gdscript-tests-self-insert ":")
|
||||
(gdscript-tests-look-at "else" -1)
|
||||
(should (= (current-indentation) 4))))
|
||||
|
||||
(ert-deftest gdscript-tests--indent-electric-colon-8 ()
|
||||
"Test indentation case where there is one more-indented previous open block, but it is comment."
|
||||
(gdscript-tests--with-temp-buffer
|
||||
"
|
||||
func f():
|
||||
if true or true:
|
||||
a = 5
|
||||
|
||||
if true and true:
|
||||
a = 10
|
||||
|
||||
# b = 3
|
||||
|
||||
else
|
||||
"
|
||||
(gdscript-tests-look-at "else")
|
||||
(goto-char (line-end-position))
|
||||
(gdscript-tests-self-insert ":")
|
||||
(gdscript-tests-look-at "else" -1)
|
||||
(should (= (current-indentation) 8))))
|
||||
|
||||
(ert-deftest gdscript-tests--dedent-if-else ()
|
||||
"Test dedentation case where else is dedented from inner if to outer if."
|
||||
(gdscript-tests--with-temp-buffer
|
||||
"
|
||||
func f():
|
||||
if true:
|
||||
if true:
|
||||
a = 5
|
||||
else:
|
||||
"
|
||||
(gdscript-tests-look-at "else")
|
||||
(let ((last-command 'indent-for-tab-command)
|
||||
(this-command 'indent-for-tab-command))
|
||||
(should (= (current-indentation) 8))
|
||||
(indent-for-tab-command)
|
||||
(should (= (current-indentation) 4)))))
|
||||
|
||||
(ert-deftest gdscript-tests--dedent-if-elif ()
|
||||
"Test dedentation case where elif is dedented from inner if to outer if."
|
||||
(gdscript-tests--with-temp-buffer
|
||||
"
|
||||
func f():
|
||||
if true:
|
||||
if true:
|
||||
a = 5
|
||||
elif true:
|
||||
"
|
||||
(gdscript-tests-look-at "elif")
|
||||
(let ((last-command 'indent-for-tab-command)
|
||||
(this-command 'indent-for-tab-command))
|
||||
(should (= (current-indentation) 8))
|
||||
(indent-for-tab-command)
|
||||
(should (= (current-indentation) 4)))))
|
||||
|
||||
(ert-deftest gdscript-tests--dedent-elif-else ()
|
||||
"Test dedentation case where else is dedented from inner elif to outer if."
|
||||
(gdscript-tests--with-temp-buffer
|
||||
"
|
||||
func f():
|
||||
if true:
|
||||
if true:
|
||||
a = 5
|
||||
elif true:
|
||||
a = 6
|
||||
else:
|
||||
"
|
||||
(gdscript-tests-look-at "else")
|
||||
(let ((last-command 'indent-for-tab-command)
|
||||
(this-command 'indent-for-tab-command))
|
||||
(should (= (current-indentation) 8))
|
||||
(indent-for-tab-command)
|
||||
(should (= (current-indentation) 4)))))
|
||||
|
||||
(ert-deftest gdscript-tests--indent-if-with-parens ()
|
||||
"Test indentation where if is followed by expression in parens."
|
||||
(gdscript-tests--with-temp-buffer
|
||||
"
|
||||
func f():
|
||||
if (
|
||||
true
|
||||
):
|
||||
aaa = 5
|
||||
"
|
||||
(gdscript-tests-look-at "aaa")
|
||||
(let ((last-command 'indent-for-tab-command)
|
||||
(this-command 'indent-for-tab-command))
|
||||
(should (= (current-indentation) 0))
|
||||
(indent-for-tab-command)
|
||||
(should (= (current-indentation) 8))
|
||||
(indent-for-tab-command)
|
||||
(should (= (current-indentation) 4)))))
|
||||
|
||||
(ert-deftest gdscript-tests--indent-elif-with-parens ()
|
||||
"Test indentation where elif is followed by expression in parens."
|
||||
(gdscript-tests--with-temp-buffer
|
||||
"
|
||||
func f():
|
||||
if (
|
||||
true
|
||||
):
|
||||
aaa = 5
|
||||
elif(
|
||||
true
|
||||
):
|
||||
aaa = 6
|
||||
"
|
||||
(gdscript-tests-look-at "aaa = 6")
|
||||
(let ((last-command 'indent-for-tab-command)
|
||||
(this-command 'indent-for-tab-command))
|
||||
(should (= (current-indentation) 0))
|
||||
(indent-for-tab-command)
|
||||
(should (= (current-indentation) 8))
|
||||
(indent-for-tab-command)
|
||||
(should (= (current-indentation) 4)))))
|
||||
|
||||
(ert-deftest gdscript-tests--indent-while-with-parens ()
|
||||
"Test indentation case where while is followed by expression in parens."
|
||||
(gdscript-tests--with-temp-buffer
|
||||
"
|
||||
func f():
|
||||
while (
|
||||
true
|
||||
):
|
||||
aaa = 5
|
||||
"
|
||||
(gdscript-tests-look-at "aaa")
|
||||
(let ((last-command 'indent-for-tab-command)
|
||||
(this-command 'indent-for-tab-command))
|
||||
(should (= (current-indentation) 0))
|
||||
(indent-for-tab-command)
|
||||
(should (= (current-indentation) 8))
|
||||
(indent-for-tab-command)
|
||||
(should (= (current-indentation) 4)))))
|
||||
|
||||
(ert-deftest gdscript-tests--indent-func-with-parens ()
|
||||
"Test indentation case where function params span multiple lines."
|
||||
(gdscript-tests--with-temp-buffer
|
||||
"
|
||||
func f(
|
||||
i: int
|
||||
):
|
||||
pass
|
||||
"
|
||||
(gdscript-tests-look-at "pass")
|
||||
(let ((last-command 'indent-for-tab-command)
|
||||
(this-command 'indent-for-tab-command))
|
||||
(should (= (current-indentation) 0))
|
||||
(indent-for-tab-command)
|
||||
(should (= (current-indentation) 4)))))
|
||||
|
||||
(ert-deftest gdscript-tests--indent-nested-ifs-with-parens ()
|
||||
"Test indentation where if is nested."
|
||||
(gdscript-tests--with-temp-buffer
|
||||
"
|
||||
func f():
|
||||
if (
|
||||
true
|
||||
):
|
||||
if (
|
||||
true
|
||||
):
|
||||
aaa
|
||||
"
|
||||
(gdscript-tests-look-at "aaa")
|
||||
(let ((last-command 'indent-for-tab-command)
|
||||
(this-command 'indent-for-tab-command))
|
||||
(should (= (current-indentation) 0))
|
||||
(indent-for-tab-command)
|
||||
(should (= (current-indentation) 12))
|
||||
(indent-for-tab-command)
|
||||
(should (= (current-indentation) 8))
|
||||
(indent-for-tab-command)
|
||||
(should (= (current-indentation) 4))
|
||||
(indent-for-tab-command)
|
||||
(should (= (current-indentation) 0))
|
||||
(indent-for-tab-command)
|
||||
(should (= (current-indentation) 12)))))
|
||||
|
||||
(ert-deftest gdscript-tests--dedent-nested-ifs-with-parens ()
|
||||
"Test dedentation where if is nested."
|
||||
(gdscript-tests--with-temp-buffer
|
||||
"
|
||||
func f():
|
||||
if (
|
||||
true
|
||||
):
|
||||
if (
|
||||
true
|
||||
):
|
||||
aaa
|
||||
else:
|
||||
"
|
||||
(gdscript-tests-look-at "else")
|
||||
(let ((last-command 'indent-for-tab-command)
|
||||
(this-command 'indent-for-tab-command))
|
||||
(should (= (current-indentation) 0))
|
||||
(indent-for-tab-command)
|
||||
(should (= (current-indentation) 8))
|
||||
(indent-for-tab-command)
|
||||
(should (= (current-indentation) 4))
|
||||
(indent-for-tab-command)
|
||||
(should (= (current-indentation) 8)))))
|
||||
@@ -100,9 +100,10 @@ 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)))
|
||||
(locate-dominating-file base-path
|
||||
(lambda (parent)
|
||||
(directory-files parent t "project.godot")))))
|
||||
(expand-file-name
|
||||
(locate-dominating-file base-path
|
||||
(lambda (parent)
|
||||
(directory-files parent t "project.godot"))))))
|
||||
|
||||
(defun gdscript-util--get-godot-project-name ()
|
||||
"Retrieve the project name from Godot's configuration file."
|
||||
@@ -113,13 +114,47 @@ WARNING: the Godot project must exist for this function to work."
|
||||
(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."
|
||||
(concat (gdscript-godot--build-shell-command) " -d "
|
||||
(file-name-sans-extension
|
||||
(concat (file-name-sans-extension
|
||||
(file-relative-name file-path
|
||||
(gdscript-util--find-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."
|
||||
(message "gdscript util read")
|
||||
(let ((p (if prompt prompt "Options")))
|
||||
(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)))))
|
||||
|
||||
(provide 'gdscript-utils)
|
||||
|
||||
;;; gdscript-utils.el ends here
|
||||
|
||||
Reference in New Issue
Block a user