diff --git a/.github/workflows/build-terminal-shared-object.yml b/.github/workflows/build-terminal-shared-object.yml new file mode 100644 index 000000000..6cba730d7 --- /dev/null +++ b/.github/workflows/build-terminal-shared-object.yml @@ -0,0 +1,43 @@ +name: Build terminal.so + +on: + push: + branches: + - main + workflow_dispatch: + +jobs: + build: + runs-on: macos-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install Homebrew + run: | + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + + - name: Install vterm + run: brew install libvterm + + - name: compile + run: | + cd extensions/terminal + gcc terminal.c -I/opt/homebrew/include -L/opt/homebrew/lib -lvterm -o terminal.so -shared -fPIC + mv terminal.so lib/macosx/arm64/terminal.so + + - name: Commit and push changes + run: | + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' + git add . + + - name: Create Pull Request + uses: peter-evans/create-pull-request@v6 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: Add terminal.so + branch: update-terminal-so-macosx-arm64 + title: Add terminal.so file + body: Update terminal.so on macosx/arm64 diff --git a/.gitignore b/.gitignore index b1a008874..50dec9aff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,6 @@ DEBUG *.fasl -*.so node_modules -package-lock.json roswell/lem-ncurses *~ @@ -28,7 +26,7 @@ GIT_IGNORE.* lem lem-ncurses -lem-rpc +lem-server *.abcl build/* @@ -36,3 +34,5 @@ build/* *.lx64fsl .qlot /systems/ + +/result diff --git a/Dockerfile b/Dockerfile index 50018bb08..23e10e351 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,9 @@ COPY . . RUN apk add --no-cache curl bash build-base ncurses-dev sbcl git +RUN curl -o quicklisp.lisp https://beta.quicklisp.org/quicklisp.lisp +RUN sbcl --noinform --no-userinit --no-sysinit --non-interactive --load quicklisp.lisp --eval "(quicklisp-quickstart:install)" --eval "(ql-util:without-prompting (ql:add-to-init-file))" + RUN curl -L https://qlot.tech/installer | bash RUN qlot install && \ diff --git a/Makefile b/Makefile index 5d03e5bee..6fae34d24 100644 --- a/Makefile +++ b/Makefile @@ -2,15 +2,30 @@ LISP ?= ${shell which sbcl} ncurses: qlot install - $(LISP) --noinform --no-sysinit --no-userinit --load .qlot/setup.lisp --load scripts/build-ncurses.lisp + $(LISP) --dynamic-space-size 4GiB --noinform --no-sysinit --no-userinit --load .qlot/setup.lisp --load scripts/build-ncurses.lisp sdl2: qlot install - $(LISP) --noinform --no-sysinit --no-userinit --load .qlot/setup.lisp --load scripts/build-sdl2.lisp + $(LISP) --dynamic-space-size 4GiB --noinform --no-sysinit --no-userinit --load .qlot/setup.lisp --load scripts/build-sdl2.lisp sdl2-ncurses: qlot install - $(LISP) --noinform --no-sysinit --no-userinit --load .qlot/setup.lisp --load scripts/build-sdl2-ncurses.lisp + $(LISP) --dynamic-space-size 4GiB --noinform --no-sysinit --no-userinit --load .qlot/setup.lisp --load scripts/build-sdl2-ncurses.lisp + +server: + qlot install + $(LISP) --dynamic-space-size 4GiB --noinform --no-sysinit --no-userinit --load .qlot/setup.lisp --load scripts/build-server.lisp + +install: + qlot install + $(LISP) --dynamic-space-size 4GiB --noinform --no-sysinit --no-userinit --load .qlot/setup.lisp --load scripts/build-sdl2-ncurses.lisp + sudo install -m 755 lem /usr/local/bin/ + sudo install -m 644 scripts/install/lem.svg /usr/share/icons/hicolor/scalable/apps/ + sudo gtk-update-icon-cache /usr/share/icons/hicolor + sudo desktop-file-install --dir=/usr/share/applications scripts/install/lem.desktop + @echo "+--------------------------------+" + @echo "| Lem installation complete! |" + @echo "+--------------------------------+" test: qlot install @@ -18,7 +33,7 @@ test: doc: qlot install - $(LISP) --noinform --no-sysinit --no-userinit --load .qlot/setup.lisp --load scripts/generate-documentation-tests.lisp --eval '(progn (lem-documentation-mode/tests::generate-markdown-file "test.md" :test) (quit))' + $(LISP) --dynamic-space-size 4GiB --noinform --no-sysinit --no-userinit --load .qlot/setup.lisp --load scripts/generate-documentation-tests.lisp --eval '(progn (lem-documentation-mode/tests::generate-markdown-file "test.md" :test) (quit))' update: git pull @@ -26,5 +41,52 @@ update: lint: .qlot/bin/sblint lem.asd - .qlot/bin/sblint lib/lisp-syntax/lem-lisp-syntax.asd + .qlot/bin/sblint extensions/asciidoc-mode/lem-asciidoc-mode.asd + .qlot/bin/sblint extensions/asm-mode/lem-asm-mode.asd + .qlot/bin/sblint extensions/c-mode/lem-c-mode.asd + .qlot/bin/sblint extensions/color-preview/lem-color-preview.asd + .qlot/bin/sblint extensions/css-mode/lem-css-mode.asd + .qlot/bin/sblint extensions/dart-mode/lem-dart-mode.asd + .qlot/bin/sblint extensions/documentation-mode/lem-documentation-mode.asd + .qlot/bin/sblint extensions/dot-mode/lem-dot-mode.asd + .qlot/bin/sblint extensions/elisp-mode/lem-elisp-mode.asd + .qlot/bin/sblint extensions/elixir-mode/lem-elixir-mode.asd +# .qlot/bin/sblint extensions/encodings/lem-encodings.asd + .qlot/bin/sblint extensions/erlang-mode/lem-erlang-mode.asd + .qlot/bin/sblint extensions/go-mode/lem-go-mode.asd + .qlot/bin/sblint extensions/haskell-mode/lem-haskell-mode.asd + .qlot/bin/sblint extensions/html-mode/lem-html-mode.asd + .qlot/bin/sblint extensions/java-mode/lem-java-mode.asd + .qlot/bin/sblint extensions/js-mode/lem-js-mode.asd + .qlot/bin/sblint extensions/json-mode/lem-json-mode.asd + .qlot/bin/sblint extensions/language-client/lem-language-client.asd + .qlot/bin/sblint extensions/language-server/lem-language-server.asd + .qlot/bin/sblint extensions/legit/lem-legit.asd .qlot/bin/sblint extensions/lisp-mode/lem-lisp-mode.asd + .qlot/bin/sblint extensions/lisp-syntax/lem-lisp-syntax.asd + .qlot/bin/sblint extensions/lsp-base/lem-lsp-base.asd + .qlot/bin/sblint extensions/lsp-mode/lem-lsp-mode.asd + .qlot/bin/sblint extensions/lua-mode/lem-lua-mode.asd + .qlot/bin/sblint extensions/makefile-mode/lem-makefile-mode.asd + .qlot/bin/sblint extensions/markdown-mode/lem-markdown-mode.asd + .qlot/bin/sblint extensions/nim-mode/lem-nim-mode.asd + .qlot/bin/sblint extensions/ocaml-mode/lem-ocaml-mode.asd + .qlot/bin/sblint extensions/paredit-mode/lem-paredit-mode.asd + .qlot/bin/sblint extensions/patch-mode/lem-patch-mode.asd + .qlot/bin/sblint extensions/posix-shell-mode/lem-posix-shell-mode.asd + .qlot/bin/sblint extensions/process/lem-process.asd + .qlot/bin/sblint extensions/python-mode/lem-python-mode.asd + .qlot/bin/sblint extensions/review-mode/lem-review-mode.asd + .qlot/bin/sblint extensions/rust-mode/lem-rust-mode.asd + .qlot/bin/sblint extensions/scala-mode/lem-scala-mode.asd + .qlot/bin/sblint extensions/scheme-mode/lem-scheme-mode.asd + .qlot/bin/sblint extensions/shell-mode/lem-shell-mode.asd + .qlot/bin/sblint extensions/sql-mode/lem-sql-mode.asd + .qlot/bin/sblint extensions/swift-mode/lem-swift-mode.asd + .qlot/bin/sblint extensions/terminal/lem-terminal.asd + .qlot/bin/sblint extensions/typescript-mode/lem-typescript-mode.asd + .qlot/bin/sblint extensions/vi-mode/lem-vi-mode.asd + .qlot/bin/sblint extensions/welcome/lem-welcome.asd + .qlot/bin/sblint extensions/xml-mode/lem-xml-mode.asd + .qlot/bin/sblint extensions/yaml-mode/lem-yaml-mode.asd + .qlot/bin/sblint extensions/ruby-mode/lem-ruby-mode.asd diff --git a/STYLEGUIDE.md b/STYLEGUIDE.md new file mode 100644 index 000000000..6cdde70f5 --- /dev/null +++ b/STYLEGUIDE.md @@ -0,0 +1,116 @@ + +Thanks for contributing to Lem! Here are some guidelines that we follow in the codebase. + +In general, we follow the Google guide: https://google.github.io/styleguide/lispguide.xml + + +## Alexandria and other utility libraries + +Lem depends on `alexandria`, so you can use `if-let`, `when-let` and similar functions. + +You can `:import-from` these symbols in your package definition, use the `alexandria:` package prefix, and define a package-local nickname. + +Try to not use new utility functions that you don't see in the codebase yet. + +For instance, we won't use `alexandria-2:line-up-first`. + +Try to not use `alexandria:curry` and prefer higher-order functions. + +## Dynamic bindings, functional style + +Avoid dynamic symbol calls (`uiop:symbol-call`) but rething your architecture instead. + +Use `defvar` and `defparameter` for user-facing variables, but avoid +using them as global variables that store state and that are used from +functions to functions. Have a more functional style, give explicit +arguments to functions. + +Example: + +```lisp +;; avoid this +(defvar *var* 1) +(defun foo () + *var*) +(let ((*var* 2)) + (foo)) +``` + +instead, have `foo` take one argument. + + +## Don't ignore compiler warnings + +Please take attention to compiler warnings. + + +## Deprecation warnings + +If you change or delete a feature, if only a variable name, you must +take care of deprecation warnings. A user should be notified that +something changed. If possible, her old config file should not fail +loading, or it should not fail without notice. Measures can vary. Ideas: + +- don't delete or rename a `defvar`, a `defparameter` or a function, but leave it and if it is used, signal a warning or an error. Add a `;; DEPRECATED` comment with the date of the comment. +- document the breaking change on the "next release changelog" issue (like https://github.com/lem-project/lem/issues/1027 or equivalent). + + +## Documentation + +Write thorough docstrings to interactive commands (`define-command`), +give meaningful documentation to important functions, give a +high-level overview in packages and comments (the "why", not the +"how"). Use the `:documentation` option of packages, generic functions +and CLOS slots. + +Please also contribute to Lem's website if you add or change a feature. + +https://github.com/lem-project/lem-project.github.io/ + +or at least, open an issue about it so we don't forget to do it, thank you. + + +## Errors in Lem + +`error` is for internal errors, and `editor-error` is displayed nicely to the user. + +## File layout + +Variables and parameters (`defvar`, `defparameter`) should be grouped +and appear near the top of the file, before conditions, classes and +functions. + +### Major and minor modes keybindings layout + +Lem modes should define all their keybindings at the top of the file. + +## Git and pull requests + +Please rebase and squash small commits together (you can do this with lem/legit ! ;) ). + +When your changes are about a mode or a feature, we like the commit message to say it upfront, for example: + + legit: add k to discard changes of unstaged files + + +## Loop + +Loop keywords are written as keywords, with the `:`: + +```lisp +(loop :for key :in … ) +``` + +## Macros, backquote, comma + +Don't write long macros, use the "call-with-" pattern. + +Don't define lists with backquote and comma, use the `list` constructor. + + +## User-visible variables names + +Parameters that can be changed by the user should not have a "-p" +suffix. Keep them for the functions' key arguments. + +They can be saved and set-able with the `lem:config` system. diff --git a/contrib/tetris/tetris.lisp b/contrib/tetris/tetris.lisp index 857a09cb2..2fabfbe89 100644 --- a/contrib/tetris/tetris.lisp +++ b/contrib/tetris/tetris.lisp @@ -66,6 +66,7 @@ (defvar *point-y*) (defvar *current-tetrimino*) (defvar *next-tetrimino*) +(defvar *hard-dropped*) (defvar *score*) (defvar *delete-nlines*) @@ -90,7 +91,8 @@ (setq *point-x* 4) (setq *point-y* -1) (setq *current-tetrimino* *next-tetrimino*) - (setq *next-tetrimino* (random-tetrimino))) + (setq *next-tetrimino* (random-tetrimino)) + (setq *hard-dropped* nil)) (defun init-field () (setq *field* @@ -254,23 +256,30 @@ (setq *current-tetrimino* tetrimino)))) (define-command tetris-move-left () () - (when *playing-p* + (when (and *playing-p* (not *hard-dropped*)) (unless (override-p (1- *point-x*) *point-y*) (decf *point-x*) (draw)))) (define-command tetris-move-right () () - (when *playing-p* + (when (and *playing-p* (not *hard-dropped*)) (unless (override-p (1+ *point-x*) *point-y*) (incf *point-x*) (draw)))) (define-command tetris-move-down () () - (when *playing-p* + (when (and *playing-p* (not *hard-dropped*)) (unless (override-p *point-x* (1+ *point-y*)) (incf *point-y*) (draw)))) +(define-command tetris-hard-drop () () + (when *playing-p* + (loop :until (override-p *point-x* (1+ *point-y*)) + :do (incf *point-y*)) + (draw) + (setf *hard-dropped* t))) + (define-command tetris-rotate () () (when *playing-p* (rotate) @@ -283,6 +292,7 @@ (define-key *tetris-mode-keymap* "Left" 'tetris-move-left) (define-key *tetris-mode-keymap* "Right" 'tetris-move-right) (define-key *tetris-mode-keymap* "Down" 'tetris-move-down) +(define-key *tetris-mode-keymap* "Space" 'tetris-hard-drop) (define-key *tetris-mode-keymap* "Up" 'tetris-rotate) (define-key *tetris-mode-keymap* "q" 'tetris-quit) diff --git a/default.nix b/default.nix new file mode 100644 index 000000000..90fefe10a --- /dev/null +++ b/default.nix @@ -0,0 +1,6 @@ +(import (let lock = builtins.fromJSON (builtins.readFile ./flake.lock); +in fetchTarball { + url = + lock.nodes.flake-compat.locked.url or "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; + sha256 = lock.nodes.flake-compat.locked.narHash; +}) { src = ./.; }).defaultNix diff --git a/docs/default-keybindings.md b/docs/default-keybindings.md index fe4a2ac42..8201fb5a2 100644 --- a/docs/default-keybindings.md +++ b/docs/default-keybindings.md @@ -1,201 +1,204 @@ ## Move | Command | Key bindings | Documentation | |---------------------------------------------------------------------------------------------------------------|---------------|-------------------------------------------------------| -| [next-line](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L64) | C-n, Down | Move the cursor to next line. | -| [next-logical-line](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L71) | | Move the cursor to the next logical line. | -| [previous-line](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L78) | C-p, Up | Move the cursor to the previous line. | -| [previous-logical-line](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L82) | | Move the cursor to the previous logical line. | -| [forward-char](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L86) | C-f, Right | Move the cursor to the next character. | -| [backward-char](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L92) | C-b, Left | Move the cursor to the previous character. | -| [move-to-beginning-of-buffer](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L97) | M-<, C-Home | Move the cursor to the beginning of the buffer. | -| [move-to-end-of-buffer](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L102) | M->, C-End | Move the cursor to the end of the buffer. | -| [move-to-beginning-of-line](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L107) | C-a, Home | Move the cursor to the beginning of the line. | -| [move-to-beginning-of-logical-line](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L123) | | Move the cursor to the beginning of the logical line. | -| [move-to-end-of-line](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L127) | C-e, End | Move the cursor to the end of the line. | -| [move-to-end-of-logical-line](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L133) | | Move the cursor to the end of the logical line. | -| [next-page](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L137) | C-v, PageDown | Move the cursor to the next page by one page. | -| [previous-page](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L145) | M-v, PageUp | Move the cursor to the previous page by one page. | -| [next-page-char](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L153) | C-x ] | Move the cursor to the next page character (^L). | -| [previous-page-char](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L163) | C-x [ | Move the cursor to the previous page character (^L). | -| [goto-line](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L167) | M-g | Move the cursor to the specified line number. | +| [next-line](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L66) | C-n, Down | Move the cursor to next line. | +| [next-logical-line](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L73) | | Move the cursor to the next logical line. | +| [previous-line](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L80) | C-p, Up | Move the cursor to the previous line. | +| [previous-logical-line](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L84) | | Move the cursor to the previous logical line. | +| [forward-char](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L88) | C-f, Right | Move the cursor to the next character. | +| [backward-char](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L94) | C-b, Left | Move the cursor to the previous character. | +| [move-to-beginning-of-buffer](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L99) | M-<, C-Home | Move the cursor to the beginning of the buffer. | +| [move-to-end-of-buffer](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L104) | M->, C-End | Move the cursor to the end of the buffer. | +| [move-to-beginning-of-line](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L109) | C-a, Home | Move the cursor to the beginning of the line. | +| [move-to-beginning-of-logical-line](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L125) | | Move the cursor to the beginning of the logical line. | +| [move-to-end-of-line](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L129) | C-e, End | Move the cursor to the end of the line. | +| [move-to-end-of-logical-line](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L135) | | Move the cursor to the end of the logical line. | +| [next-page](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L139) | C-v, PageDown | Move the cursor to the next page by one page. | +| [previous-page](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L147) | M-v, PageUp | Move the cursor to the previous page by one page. | +| [next-page-char](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L155) | C-x ] | Move the cursor to the next page character (^L). | +| [previous-page-char](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L165) | C-x [ | Move the cursor to the previous page character (^L). | +| [goto-line](https://github.com/lem-project/lem/blob/main/src/commands/move.lisp#L169) | M-g | Move the cursor to the specified line number. | ## Edit | Command | Key bindings | Documentation | |--------------------------------------------------------------------------------------------------------|-------------------|--------------------------------------------------------------------------------------| -| [self-insert](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L71) | | Processes the key entered. | -| [newline](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L89) | Return | Insert a new line. | -| [open-line](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L93) | C-o | Insert a new line without moving the cursor position. | -| [quoted-insert](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L97) | C-q | Insert the next entered key (including control characters). | -| [delete-next-char](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L112) | C-d, Delete | Delete the next character. | -| [delete-previous-char](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L135) | C-h, Backspace | Delete the previous character. | -| [copy-region](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L149) | M-w | Copy the text of region. | -| [copy-region-to-clipboard](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L155) | | Copy the selected text to the clipboard. | -| [kill-region](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L167) | C-w | Kill the text of region. | -| [kill-region-to-clipboard](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L176) | | Kill the text of region and copy to the clipboard. | -| [kill-line](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L181) | C-k | Kill from the current cursor position to the end of the line. | -| [kill-whole-line](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L213) | C-Shift-Backspace | Kill the entire line and the remaining whitespace | -| [yank](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L232) | C-y | Paste the copied text. | -| [yank-pop](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L236) | M-y | Replaces the immediately pasted text with the next text in the killring. | -| [yank-pop-next](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L249) | | Replaces the immediately preceding yank-pop text with the text before the kill ring. | -| [yank-to-clipboard](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L262) | | Copy the text of the killring to the clipboard. | -| [paste-from-clipboard](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L269) | | Inserts text from the clipboard. | -| [entab-line](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L287) | | Replaces the indent of the current line from space to tab. | -| [detab-line](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L293) | | Replaces the indent of the current line from tab to space. | -| [delete-blank-lines](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L300) | C-x C-o | Delete blank lines before and after the cursor. | -| [just-one-space](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L324) | M-Space | | -| [delete-indentation](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L330) | M-^ | Merge the current line with the previous line. | -| [transpose-characters](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L350) | C-t | Swaps the characters before and after the cursor. | -| [undo](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L367) | C-\ | Undo. | -| [redo](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L374) | C-_, C-/ | Redo. | -| [delete-trailing-whitespace](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L401) | | Removes all end-of-line and end-of-buffer whitespace from the current buffer. | +| [self-insert](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L73) | | Processes the key entered. | +| [newline](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L91) | Return | Insert a new line. | +| [open-line](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L95) | C-o | Insert a new line without moving the cursor position. | +| [quoted-insert](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L99) | C-q | Insert the next entered key (including control characters). | +| [delete-next-char](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L114) | C-d, Delete | Delete the next character. | +| [delete-previous-char](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L137) | C-h, Backspace | Delete the previous character. | +| [copy-region](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L151) | M-w | Copy the text of region. | +| [copy-region-to-clipboard](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L157) | | Copy the selected text to the clipboard. | +| [kill-region](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L169) | C-w | Kill the text of region. | +| [kill-region-to-clipboard](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L178) | | Kill the text of region and copy to the clipboard. | +| [kill-line](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L183) | C-k | Kill from the current cursor position to the end of the line. | +| [kill-whole-line](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L215) | C-Shift-Backspace | Kill the entire line and the remaining whitespace | +| [yank](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L234) | C-y | Paste the copied text. | +| [yank-pop](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L238) | M-y | Replaces the immediately pasted text with the next text in the killring. | +| [yank-pop-next](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L251) | | Replaces the immediately preceding yank-pop text with the text before the kill ring. | +| [yank-to-clipboard](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L264) | | Copy the text of the killring to the clipboard. | +| [paste-from-clipboard](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L271) | | Inserts text from the clipboard. | +| [entab-line](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L289) | | Replaces the indent of the current line from space to tab. | +| [detab-line](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L295) | | Replaces the indent of the current line from tab to space. | +| [delete-blank-lines](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L302) | C-x C-o | Delete blank lines before and after the cursor. | +| [just-one-space](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L326) | M-Space | Combines consecutive whitespace before and after the cursor into one. | +| [delete-indentation](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L332) | M-^ | Merge the current line with the previous line. | +| [transpose-characters](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L352) | C-t | Swaps the characters before and after the cursor. | +| [undo](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L369) | C-\ | Undo. | +| [redo](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L376) | C-_, C-/ | Redo. | +| [delete-trailing-whitespace](https://github.com/lem-project/lem/blob/main/src/commands/edit.lisp#L403) | | Removes all end-of-line and end-of-buffer whitespace from the current buffer. | ## Mark | Command | Key bindings | Documentation | |--------------------------------------------------------------------------------------------------|--------------|----------------------------------------------------------------| -| [mark-set](https://github.com/lem-project/lem/blob/main/src/commands/mark.lisp#L13) | C-@, C-Space | Sets a mark at the current cursor position. | -| [exchange-point-mark](https://github.com/lem-project/lem/blob/main/src/commands/mark.lisp#L19) | C-x C-x | Exchange the current cursor position with the marked position. | -| [mark-set-whole-buffer](https://github.com/lem-project/lem/blob/main/src/commands/mark.lisp#L27) | C-x h | Select the whole buffer as a region. | +| [mark-set](https://github.com/lem-project/lem/blob/main/src/commands/mark.lisp#L15) | C-@, C-Space | Sets a mark at the current cursor position. | +| [exchange-point-mark](https://github.com/lem-project/lem/blob/main/src/commands/mark.lisp#L21) | C-x C-x | Exchange the current cursor position with the marked position. | +| [mark-set-whole-buffer](https://github.com/lem-project/lem/blob/main/src/commands/mark.lisp#L29) | C-x h | Select the whole buffer as a region. | ## Word | Command | Key bindings | Documentation | |--------------------------------------------------------------------------------------------------|---------------------------------|-----------------------------------------------------------| -| [forward-word](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L82) | M-f, C-Right | Move to cursor to next word. | -| [previous-word](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L86) | M-b, C-Left | Move to cursor to previous word | -| [delete-word](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L90) | M-d, C-Delete | Delete the next word. | -| [backward-delete-word](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L104) | M-C-h, M-Backspace, C-Backspace | Delete the previous word. | -| [downcase-region](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L135) | C-x C-l | Replaces the selected region with a downcase. | -| [uppercase-region](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L139) | C-x C-u | Replaces the selected region with a uppercase. | -| [capitalize-word](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L160) | M-c | Replace the following word with capital-case. | -| [lowercase-word](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L164) | M-l | Replace the following word with lowercase. | -| [uppercase-word](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L168) | M-u | Replace the following word with uppercase. | -| [forward-paragraph](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L172) | M-} | Move cursor to forward paragraph. | -| [backward-paragraph](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L185) | M-{ | Move cursor to backward paragraph. | -| [kill-paragraph](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L189) | M-k | Kill the forward paragraph. | -| [count-words](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L205) | M-= | Count the number of lines/words/characters in the buffer. | +| [forward-word](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L84) | M-f, C-Right | Move to cursor to next word. | +| [previous-word](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L88) | M-b, C-Left | Move to cursor to previous word | +| [delete-word](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L92) | M-d, C-Delete | Delete the next word. | +| [backward-delete-word](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L106) | M-C-h, M-Backspace, C-Backspace | Delete the previous word. | +| [downcase-region](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L137) | C-x C-l | Replaces the selected region with a downcase. | +| [uppercase-region](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L141) | C-x C-u | Replaces the selected region with a uppercase. | +| [capitalize-word](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L162) | M-c | Replace the following word with capital-case. | +| [lowercase-word](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L166) | M-l | Replace the following word with lowercase. | +| [uppercase-word](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L170) | M-u | Replace the following word with uppercase. | +| [forward-paragraph](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L174) | M-} | Move cursor to forward paragraph. | +| [backward-paragraph](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L187) | M-{ | Move cursor to backward paragraph. | +| [kill-paragraph](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L191) | M-k | Kill the forward paragraph. | +| [count-words](https://github.com/lem-project/lem/blob/main/src/commands/word.lisp#L207) | M-= | Count the number of lines/words/characters in the buffer. | ## S-Expression | Command | Key bindings | Documentation | |----------------------------------------------------------------------------------------------------|------------------|---------------------------------------------------| -| [forward-sexp](https://github.com/lem-project/lem/blob/main/src/commands/s-expression.lisp#L28) | M-C-f | Move the cursor to the forward expression. | -| [backward-sexp](https://github.com/lem-project/lem/blob/main/src/commands/s-expression.lisp#L39) | M-C-b | Move the cursor to the backward expression. | -| [forward-list](https://github.com/lem-project/lem/blob/main/src/commands/s-expression.lisp#L43) | M-C-n | Move the cursor to the forward list. | -| [backward-list](https://github.com/lem-project/lem/blob/main/src/commands/s-expression.lisp#L47) | M-C-p | Move the cursor to the backward list. | -| [down-list](https://github.com/lem-project/lem/blob/main/src/commands/s-expression.lisp#L51) | M-C-d | Move the cursor to the inner expression. | -| [up-list](https://github.com/lem-project/lem/blob/main/src/commands/s-expression.lisp#L55) | M-C-u | Move the cursor to the outer expression. | -| [mark-sexp](https://github.com/lem-project/lem/blob/main/src/commands/s-expression.lisp#L60) | M-C-@, M-C-Space | Select the forward expression as a region. | -| [kill-sexp](https://github.com/lem-project/lem/blob/main/src/commands/s-expression.lisp#L70) | M-C-k | Kill the forward expression as a region. | -| [transpose-sexps](https://github.com/lem-project/lem/blob/main/src/commands/s-expression.lisp#L79) | M-C-t | Swaps the expression before and after the cursor. | +| [forward-sexp](https://github.com/lem-project/lem/blob/main/src/commands/s-expression.lisp#L30) | M-C-f | Move the cursor to the forward expression. | +| [backward-sexp](https://github.com/lem-project/lem/blob/main/src/commands/s-expression.lisp#L41) | M-C-b | Move the cursor to the backward expression. | +| [forward-list](https://github.com/lem-project/lem/blob/main/src/commands/s-expression.lisp#L45) | M-C-n | Move the cursor to the forward list. | +| [backward-list](https://github.com/lem-project/lem/blob/main/src/commands/s-expression.lisp#L49) | M-C-p | Move the cursor to the backward list. | +| [down-list](https://github.com/lem-project/lem/blob/main/src/commands/s-expression.lisp#L53) | M-C-d | Move the cursor to the inner expression. | +| [up-list](https://github.com/lem-project/lem/blob/main/src/commands/s-expression.lisp#L57) | M-C-u | Move the cursor to the outer expression. | +| [mark-sexp](https://github.com/lem-project/lem/blob/main/src/commands/s-expression.lisp#L62) | M-C-@, M-C-Space | Select the forward expression as a region. | +| [kill-sexp](https://github.com/lem-project/lem/blob/main/src/commands/s-expression.lisp#L72) | M-C-k | Kill the forward expression as a region. | +| [transpose-sexps](https://github.com/lem-project/lem/blob/main/src/commands/s-expression.lisp#L81) | M-C-t | Swaps the expression before and after the cursor. | ## File | Command | Key bindings | Documentation | |---------------------------------------------------------------------------------------------------|--------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [find-file](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L67) | C-x C-f | Open the file. | -| [find-file-recursively](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L210) | | Open a file, from the list of all files present under the buffer's directory, recursively. | -| [read-file](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L226) | C-x C-r | Open the file as a read-only. | -| [save-current-buffer](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L264) | C-x C-s | Saves the current buffer text to a file | -| [write-file](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L270) | C-x C-w | Saves the text in the current buffer to the specified file | -| [write-region-file](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L291) | | Saves the region of text to the specified file | -| [insert-file](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L299) | C-x Tab | Inserts the contents of the file into the current buffer. | -| [save-some-buffers](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L305) | C-x s | Save some files in the open buffer. | -| [revert-buffer](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L337) | | Restores the buffer. Normally this command will cause the contents of the file to be reflected in the buffer. | -| [change-directory](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L370) | | | -| [current-directory](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L379) | | Display the directory of the active buffer. +| [find-file](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L69) | C-x C-f | Open the file. | +| [find-file-recursively](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L212) | | Open a file, from the list of all files present under the buffer's directory, recursively. | +| [read-file](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L228) | C-x C-r | Open the file as a read-only. | +| [save-current-buffer](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L266) | C-x C-s | Saves the current buffer text to a file | +| [write-file](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L272) | C-x C-w | Saves the text in the current buffer to the specified file | +| [write-region-file](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L293) | | Saves the region of text to the specified file | +| [insert-file](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L301) | C-x Tab | Inserts the contents of the file into the current buffer. | +| [save-some-buffers](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L307) | C-x s | Save some files in the open buffer. | +| [revert-buffer](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L339) | | Restores the buffer. Normally this command will cause the contents of the file to be reflected in the buffer. | +| [change-directory](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L372) | | Change directories associated with the buffer. | +| [current-directory](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L381) | | Display the directory of the active buffer. With prefix argument INSERT, insert the directory of the active buffer at point. | -| [format-current-buffer](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L387) | | Save changes and try to format the current buffer. +| [format-current-buffer](https://github.com/lem-project/lem/blob/main/src/commands/file.lisp#L389) | | Save changes and try to format the current buffer. Supported modes include: c-mode with clang-format, go-mode with gofmt, js-mode and json-mode with prettier, and lisp-mode. Additionally rust-mode uses rustfmt. | ## Project | Command | Key bindings | Documentation | |-------------------------------------------------------------------------------------------------------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [project-find-file](https://github.com/lem-project/lem/blob/main/src/commands/project.lisp#L132) | C-x p f | Open a file, from the list of all files in this project. | -| [project-root](https://github.com/lem-project/lem/blob/main/src/commands/project.lisp#L149) | | Display this buffer's project directory. | -| [project-root-directory](https://github.com/lem-project/lem/blob/main/src/commands/project.lisp#L156) | C-x p d | Open this project's root directory. | -| [project-kill-buffers](https://github.com/lem-project/lem/blob/main/src/commands/project.lisp#L216) | C-x p K | Delete all this project's buffers, except: +| [project-find-file](https://github.com/lem-project/lem/blob/main/src/commands/project.lisp#L137) | C-x p f | Open a file, from the list of all files in this project. | +| [project-root](https://github.com/lem-project/lem/blob/main/src/commands/project.lisp#L154) | | Display this buffer's project directory. | +| [project-root-directory](https://github.com/lem-project/lem/blob/main/src/commands/project.lisp#L161) | C-x p d | Open this project's root directory. | +| [project-kill-buffers](https://github.com/lem-project/lem/blob/main/src/commands/project.lisp#L221) | C-x p K | Delete all this project's buffers, except: - if *delete-repl-buffer* is non t, we don't delete the REPL buffer. - if *delete-last-buffer* is non nil, we will delete the last buffer. This would cause Lem to exit. | +| [project-save](https://github.com/lem-project/lem/blob/main/src/commands/project.lisp#L269) | C-x p s | Remember the current project for later sessions. | +| [project-unsave](https://github.com/lem-project/lem/blob/main/src/commands/project.lisp#L274) | C-x p u | Prompt for a project and remove it from the list of saved projects. | +| [project-switch](https://github.com/lem-project/lem/blob/main/src/commands/project.lisp#L292) | C-x p p | Prompt for a saved project and find a file in this project. | ## Buffer | Command | Key bindings | Documentation | |----------------------------------------------------------------------------------------------------|--------------|-----------------------------------------------| -| [indent-current-buffer](https://github.com/lem-project/lem/blob/main/src/commands/buffer.lisp#L15) | | Indent the current buffer. | -| [toggle-read-only](https://github.com/lem-project/lem/blob/main/src/commands/buffer.lisp#L19) | C-x C-q | Toggle the buffer read-only. | -| [rename-buffer](https://github.com/lem-project/lem/blob/main/src/commands/buffer.lisp#L27) | | Rename the buffer. | -| [unmark-buffer](https://github.com/lem-project/lem/blob/main/src/commands/buffer.lisp#L31) | M-~ | Remove the mark where the buffer was changed. | +| [indent-current-buffer](https://github.com/lem-project/lem/blob/main/src/commands/buffer.lisp#L17) | | Indent the current buffer. | +| [toggle-read-only](https://github.com/lem-project/lem/blob/main/src/commands/buffer.lisp#L21) | C-x C-q | Toggle the buffer read-only. | +| [rename-buffer](https://github.com/lem-project/lem/blob/main/src/commands/buffer.lisp#L29) | | Rename the buffer. | +| [unmark-buffer](https://github.com/lem-project/lem/blob/main/src/commands/buffer.lisp#L33) | M-~ | Remove the mark where the buffer was changed. | ## Window | Command | Key bindings | Documentation | |----------------------------------------------------------------------------------------------------------------|----------------|-----------------------------------------------------------------------| -| [select-buffer](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L66) | C-x b | Switches to the selected buffer. | -| [kill-buffer](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L93) | C-x k | Delete buffer. | -| [previous-buffer](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L102) | C-x Left | Switches to the previous buffer. | -| [next-buffer](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L112) | C-x Right | Switches to the next buffer. | -| [recenter](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L118) | C-l | Scroll so that the cursor is in the middle. | -| [split-active-window-vertically](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L125) | C-x 2 | Split the current window vertically. | -| [split-active-window-horizontally](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L131) | C-x 3 | Split the current window horizontally. | -| [next-window](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L137) | C-x o, M-o | Go to the next window. | -| [previous-window](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L149) | M-O | | -| [switch-to-last-focused-window](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L152) | | Go to the window that was last in focus. | -| [window-move-down](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L160) | | Go to the window on the down. | -| [window-move-up](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L165) | | Go to the window on the up. | -| [window-move-right](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L170) | | Go to the window on the right. | -| [window-move-left](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L175) | | Go to the window on the left. | -| [delete-other-windows](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L180) | C-x 1 | Delete all other windows. | -| [delete-active-window](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L193) | C-x 0, M-q | Delete the active window. | -| [quit-active-window](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L198) | | Quit the active window. This is a command for a popped-up window. | -| [grow-window](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L203) | C-x ^ | Grow the window's height. | -| [shrink-window](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L211) | C-x C-z | Shrink the window's height. | -| [grow-window-horizontally](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L219) | C-x } | Grow the window's width. | -| [shrink-window-horizontally](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L227) | C-x { | Shrink the window's width. | -| [scroll-down](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L238) | C-Down, M-Down | Scroll down. | -| [scroll-up](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L252) | C-Up, M-Up | Scroll up. | -| [find-file-next-window](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L264) | C-x 4 f | Open a file in another window. Split the screen vertically if needed. | -| [read-file-next-window](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L265) | C-x 4 r | Read a file in another window. | -| [select-buffer-next-window](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L266) | C-x 4 b | Select a buffer in another window. | -| [compare-windows](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L270) | | | +| [select-buffer](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L68) | C-x b | Switches to the selected buffer. | +| [kill-buffer](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L95) | C-x k | Delete buffer. | +| [previous-buffer](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L104) | C-x Left | Switches to the previous buffer. | +| [next-buffer](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L114) | C-x Right | Switches to the next buffer. | +| [recenter](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L120) | C-l | Scroll so that the cursor is in the middle. | +| [split-active-window-vertically](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L127) | C-x 2 | Split the current window vertically. | +| [split-active-window-horizontally](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L133) | C-x 3 | Split the current window horizontally. | +| [next-window](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L139) | C-x o, M-o | Go to the next window. | +| [previous-window](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L151) | M-O | | +| [switch-to-last-focused-window](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L154) | | Go to the window that was last in focus. | +| [window-move-down](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L162) | | Go to the window on the down. | +| [window-move-up](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L167) | | Go to the window on the up. | +| [window-move-right](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L172) | | Go to the window on the right. | +| [window-move-left](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L177) | | Go to the window on the left. | +| [delete-other-windows](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L182) | C-x 1 | Delete all other windows. | +| [delete-active-window](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L195) | C-x 0, M-q | Delete the active window. | +| [quit-active-window](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L200) | | Quit the active window. This is a command for a popped-up window. | +| [grow-window](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L205) | C-x ^ | Grow the window's height. | +| [shrink-window](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L213) | C-x C-z | Shrink the window's height. | +| [grow-window-horizontally](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L221) | C-x } | Grow the window's width. | +| [shrink-window-horizontally](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L229) | C-x { | Shrink the window's width. | +| [scroll-down](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L240) | C-Down, M-Down | Scroll down. | +| [scroll-up](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L254) | C-Up, M-Up | Scroll up. | +| [find-file-next-window](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L266) | C-x 4 f | Open a file in another window. Split the screen vertically if needed. | +| [read-file-next-window](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L267) | C-x 4 r | Read a file in another window. | +| [select-buffer-next-window](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L268) | C-x 4 b | Select a buffer in another window. | +| [compare-windows](https://github.com/lem-project/lem/blob/main/src/commands/window.lisp#L272) | | | ## Multiple-Cursors -| Command | Key bindings | Documentation | -|----------------------------------------------------------------------------------------------------------------|--------------|-------------------------------------------------------------| -| [add-cursors-to-next-line](https://github.com/lem-project/lem/blob/main/src/commands/multiple-cursors.lisp#L8) | M-C | Duplicates the cursor under the currently existing cursors. | +| Command | Key bindings | Documentation | +|-----------------------------------------------------------------------------------------------------------------|--------------|-------------------------------------------------------------| +| [add-cursors-to-next-line](https://github.com/lem-project/lem/blob/main/src/commands/multiple-cursors.lisp#L10) | M-C | Duplicates the cursor under the currently existing cursors. | ## Process | Command | Key bindings | Documentation | |---------------------------------------------------------------------------------------------|--------------|---------------------------------------------------------------------------------------| -| [filter-buffer](https://github.com/lem-project/lem/blob/main/src/commands/process.lisp#L10) | C-x # | Replaces the contents of the buffer with the result of executing the command entered. | -| [pipe-command](https://github.com/lem-project/lem/blob/main/src/commands/process.lisp#L43) | C-x @ | Run a command and displays the output. | +| [filter-buffer](https://github.com/lem-project/lem/blob/main/src/commands/process.lisp#L12) | C-x # | Replaces the contents of the buffer with the result of executing the command entered. | +| [pipe-command](https://github.com/lem-project/lem/blob/main/src/commands/process.lisp#L45) | C-x @ | Run a command and displays the output. | ## Help | Command | Key bindings | Documentation | |----------------------------------------------------------------------------------------------|--------------|------------------------------------------------------------------------------| -| [describe-key](https://github.com/lem-project/lem/blob/main/src/commands/help.lisp#L12) | C-x ? | Tell what is the command associated to a keybinding. | -| [describe-bindings](https://github.com/lem-project/lem/blob/main/src/commands/help.lisp#L41) | | Describe the bindings of the buffer's current major mode. | -| [list-modes](https://github.com/lem-project/lem/blob/main/src/commands/help.lisp#L63) | | Output all available major and minor modes. | -| [apropos-command](https://github.com/lem-project/lem/blob/main/src/commands/help.lisp#L110) | | Find all symbols in the running Lisp image whose names match a given string. | -| [lem-version](https://github.com/lem-project/lem/blob/main/src/commands/help.lisp#L121) | | Display Lem's version. | +| [describe-key](https://github.com/lem-project/lem/blob/main/src/commands/help.lisp#L14) | C-x ? | Tell what is the command associated to a keybinding. | +| [describe-bindings](https://github.com/lem-project/lem/blob/main/src/commands/help.lisp#L43) | | Describe the bindings of the buffer's current major mode. | +| [list-modes](https://github.com/lem-project/lem/blob/main/src/commands/help.lisp#L65) | | Output all available major and minor modes. | +| [apropos-command](https://github.com/lem-project/lem/blob/main/src/commands/help.lisp#L112) | | Find all symbols in the running Lisp image whose names match a given string. | +| [lem-version](https://github.com/lem-project/lem/blob/main/src/commands/help.lisp#L123) | | Display Lem's version. | ## Font | Command | Key bindings | Documentation | |-----------------------------------------------------------------------------------------------|--------------|----------------------------------------------------------------------| -| [font-size-increase](https://github.com/lem-project/lem/blob/main/src/commands/font.lisp#L10) | C-+ | Make the font larger (this currently only works with SDL2 frontend) | -| [font-size-decrease](https://github.com/lem-project/lem/blob/main/src/commands/font.lisp#L14) | C-- | Make the font smaller (this currently only works with SDL2 frontend) | +| [font-size-increase](https://github.com/lem-project/lem/blob/main/src/commands/font.lisp#L12) | C-+ | Make the font larger (this currently only works with SDL2 frontend) | +| [font-size-decrease](https://github.com/lem-project/lem/blob/main/src/commands/font.lisp#L16) | C-- | Make the font smaller (this currently only works with SDL2 frontend) | ## Other | Command | Key bindings | Documentation | |-----------------------------------------------------------------------------------------------|----------------|--------------------------------------------------------------| -| [nop-command](https://github.com/lem-project/lem/blob/main/src/commands/other.lisp#L22) | NopKey | | -| [undefined-key](https://github.com/lem-project/lem/blob/main/src/commands/other.lisp#L25) | | Signal undefined key error. | -| [keyboard-quit](https://github.com/lem-project/lem/blob/main/src/commands/other.lisp#L29) | C-g | Signal a `quit` condition. | -| [escape](https://github.com/lem-project/lem/blob/main/src/commands/other.lisp#L33) | Escape | Signal a `quit` condition silently. | -| [exit-lem](https://github.com/lem-project/lem/blob/main/src/commands/other.lisp#L37) | C-x C-c | Ask for modified buffers before exiting lem. | -| [quick-exit](https://github.com/lem-project/lem/blob/main/src/commands/other.lisp#L50) | | Exit the lem job and kill it. | -| [execute-command](https://github.com/lem-project/lem/blob/main/src/commands/other.lisp#L55) | M-x | Read a command name, then read the ARG and call the command. | -| [show-context-menu](https://github.com/lem-project/lem/blob/main/src/commands/other.lisp#L74) | Shift-F10, M-h | | -| [load-library](https://github.com/lem-project/lem/blob/main/src/commands/other.lisp#L80) | | Load the Lisp library named NAME. | +| [nop-command](https://github.com/lem-project/lem/blob/main/src/commands/other.lisp#L24) | NopKey | | +| [undefined-key](https://github.com/lem-project/lem/blob/main/src/commands/other.lisp#L27) | | Signal undefined key error. | +| [keyboard-quit](https://github.com/lem-project/lem/blob/main/src/commands/other.lisp#L31) | C-g | Signal a `quit` condition. | +| [escape](https://github.com/lem-project/lem/blob/main/src/commands/other.lisp#L35) | Escape | Signal a `quit` condition silently. | +| [exit-lem](https://github.com/lem-project/lem/blob/main/src/commands/other.lisp#L39) | C-x C-c | Ask for modified buffers before exiting lem. | +| [quick-exit](https://github.com/lem-project/lem/blob/main/src/commands/other.lisp#L52) | | Exit the lem job and kill it. | +| [execute-command](https://github.com/lem-project/lem/blob/main/src/commands/other.lisp#L57) | M-x | Read a command name, then read the ARG and call the command. | +| [show-context-menu](https://github.com/lem-project/lem/blob/main/src/commands/other.lisp#L76) | Shift-F10, M-h | | +| [load-library](https://github.com/lem-project/lem/blob/main/src/commands/other.lisp#L82) | | Load the Lisp library named NAME. | ## Frame | Command | Key bindings | Documentation | |----------------------------------------------------------------------------------------------------|--------------|---------------------| -| [toggle-frame-fullscreen](https://github.com/lem-project/lem/blob/main/src/commands/frame.lisp#L6) | | Toggles fullscreen. | +| [toggle-frame-fullscreen](https://github.com/lem-project/lem/blob/main/src/commands/frame.lisp#L8) | | Toggles fullscreen. | diff --git a/extensions/asciidoc-mode/asciidoc-mode.lisp b/extensions/asciidoc-mode/asciidoc-mode.lisp index dbfd54317..ac34e9c9d 100644 --- a/extensions/asciidoc-mode/asciidoc-mode.lisp +++ b/extensions/asciidoc-mode/asciidoc-mode.lisp @@ -64,4 +64,4 @@ link : https://asciidoctor.org/docs/asciidoc-syntax-quick-reference/ :mode-hook *asciidoc-mode-hook*) (setf (variable-value 'enable-syntax-highlight) t)) -(define-file-type ("adoc") 'asciidoc-mode) +(define-file-type ("adoc") asciidoc-mode) diff --git a/extensions/color-preview/color-preview.lisp b/extensions/color-preview/color-preview.lisp index a8293f6ca..09946e094 100644 --- a/extensions/color-preview/color-preview.lisp +++ b/extensions/color-preview/color-preview.lisp @@ -27,11 +27,7 @@ (move-to-position start start-pos) (move-to-position end end-pos) (delete-between-points start end) - (insert-string start - (format nil "#~2,'0X~2,'0X~2,'0X" - (color-red color) - (color-green color) - (color-blue color))) + (insert-string start (color-to-hex-string color)) (scan-color-in-window window))))))))) (define-overlay-accessors color-ovelray diff --git a/extensions/copilot/client.lisp b/extensions/copilot/client.lisp new file mode 100644 index 000000000..8b4073d38 --- /dev/null +++ b/extensions/copilot/client.lisp @@ -0,0 +1,221 @@ +(uiop:define-package :lem-copilot/client + (:use :cl + :lem-copilot/utils + :lem-copilot/logger) + (:export :run-client + :client-process + :connect + :request + :request-async + :notify + :initialize + :initialized + :set-editor-info + :sign-in-initiate + :sign-in-confirm + :check-status + :text-document/did-open + :text-document/did-close + :text-document/did-change + :text-document/did-focus + :get-completions + :notify-shown + :notify-accepted + :notify-rejected + :get-completions-cycling + :+trigger-kind.invoked+ + :+trigger-kind.trigger-character+ + :text-document/inline-completion + :text-document/did-show-completion + :$/cancel-request)) +(in-package :lem-copilot/client) + +(defparameter *logging-output* t) +(defparameter *logging-request* nil) +(defparameter *display-copilot-warning* t) + +(defstruct client + jsonrpc + process + stream) + +(defun run-client (&key process) + (let ((stream (lem-lsp-mode/async-process-stream:make-input-stream + process + :logger (when *logging-output* 'logger))) + (client (jsonrpc:make-client))) + (make-client :jsonrpc client + :process process + :stream stream))) + +(defun request-log (type method params) + (when *logging-request* + (do-log (format nil "~A ~A ~A~%" type method (pretty-json params))))) + +(defun display-copilot-warning () + (when *display-copilot-warning* + (lem:display-popup-message + (format nil + "~{~A~^~%~}" + '("Copilot has issued a warning." + "If it does not work properly, please execute `M-x copilot-restart`." + "" + "To view the copilot log, execute `M-x test/copilot-log`.")) + :style '(:gravity :top) + :timeout 10))) + +(defun logger (output) + (when (search "MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 change listeners added to [EventEmitter]." output) + (display-copilot-warning)) + (do-log output)) + +(defun connect (client) + (jsonrpc/client:client-connect-using-class + (client-jsonrpc client) + 'lem-lsp-mode/lem-stdio-transport:lem-stdio-transport + :process (client-process client) + :stream (client-stream client))) + +(defun request (client method params) + (request-log :request method params) + (jsonrpc:call (client-jsonrpc client) method params)) + +(defun request-async (client method params &key callback error-callback) + (request-log :request-async method params) + (jsonrpc:call-async (client-jsonrpc client) + method + params + callback + error-callback)) + +(defun notify (client method params) + (request-log :notify method params) + (jsonrpc:notify (client-jsonrpc client) method params)) + +(defun initialize (client &key callback) + (request-async client + "initialize" + (hash "capabilities" + (hash "workspace" + (hash "workspaceFolders" t + "editorConfiguration" (hash "enableAutoCompletions" t)))) + :callback callback)) + +(defun initialized (client) + (notify client "initialized" (hash))) + +(defun set-editor-info (client &key callback) + (request-async client + "setEditorInfo" + (hash "editorInfo" + (hash "name" "Lem" + "version" (lem:get-version-string)) + "editorPluginInfo" (hash "name" "lem-copilot" + "version" "0.0")) + :callback callback)) + +(defun sign-in-initiate (client) + (request client + "signInInitiate" + (hash))) + +(defun sign-in-confirm (client user-code &key callback) + (request-async client + "signInConfirm" + (hash "userCode" user-code) + :callback callback + :error-callback #'default-error-callback)) + +(defun check-status (client) + (request client + "checkStatus" + (hash))) + +(defun text-document/did-open (client &key uri language-id version text) + (notify client + "textDocument/didOpen" + (hash "textDocument" (hash "uri" uri + "languageId" language-id + "version" version + "text" text)))) + +(defun text-document/did-close (client &key uri) + (notify client + "textDocument/didClose" + (hash "textDocument" (hash "uri" uri)))) + +(defun text-document/did-change (client &key uri version content-changes) + (notify client + "textDocument/didChange" + (hash "textDocument" (hash "uri" uri + "version" version) + "contentChanges" content-changes))) + +(defun text-document/did-focus (client &key uri) + (notify client + "textDocument/didFocus" + (hash "textDocument" (hash "uri" uri)))) + +(defun get-completions (client &key doc callback) + (request-async client + "getCompletions" + (hash "doc" doc) + :callback callback + :error-callback #'default-error-callback)) + +(defun notify-shown (client uuid) + (request-async client "notifyShown" (hash "uuid" uuid))) + +(defun notify-accepted (client uuid) + (request-async client + "notifyAccepted" + (hash "uuid" uuid) + :error-callback #'default-error-callback)) + +(defun notify-rejected (client uuid) + (request-async client + "notifyRejected" + (hash "uuids" (vector uuid)) + :error-callback #'default-error-callback)) + +(defun get-completions-cycling (client &key doc callback error-callback) + (request-async client + "getCompletionsCycling" + (hash "doc" doc) + :callback callback + :error-callback error-callback)) + +(defparameter +trigger-kind.invoked+ 1) +(defparameter +trigger-kind.trigger-character+ 1) + +(defun text-document/inline-completion + (client &key callback + error-callback + uri + position + insert-spaces + tab-size + (trigger-kind +trigger-kind.trigger-character+)) + (request-async client + "textDocument/inlineCompletion" + (hash "textDocument" (hash "uri" uri) + "position" position + "formattingOptions" (hash "insertSpaces" insert-spaces + "tabSize" tab-size) + "context" (hash "triggerKind" trigger-kind)) + :callback callback + :error-callback (when error-callback (lambda (&rest args) + (apply error-callback args))))) + +(defun text-document/did-show-completion (client item) + (notify client + "textDocument/didShowCompletion" + (hash "item" item))) + +(defun $/cancel-request (client id) + (notify client + "$/cancelRequest" + (hash "id" id))) + +(defun default-error-callback (&rest args) + (lem:send-event (lambda () (lem:message "~A" args)))) diff --git a/extensions/copilot/copilot.lisp b/extensions/copilot/copilot.lisp new file mode 100644 index 000000000..03e6dfd3d --- /dev/null +++ b/extensions/copilot/copilot.lisp @@ -0,0 +1,422 @@ +(defpackage :lem-copilot + (:use :cl + :lem + :lem-copilot/utils) + (:local-nicknames (:client :lem-copilot/client) + (:logger :lem-copilot/logger))) +(in-package :lem-copilot) + +(define-attribute suggestion-attribute + (t :foreground "dark gray")) + +(define-attribute cycling-attribute + (t :foreground "green")) + +(defvar *client* nil) + +(defun client () + *client*) + +(defun copilot-root () + (merge-pathnames "copilot/" (lem-home))) + +(defun copilot-path () + (merge-pathnames "lib/node_modules/copilot-node-server/copilot/dist/language-server.js" + (copilot-root))) + +(defun installed-copilot-server-p () + (uiop:file-exists-p (copilot-path))) + +(defun run-process () + (async-process:create-process (list "node" + (namestring (copilot-path)) + "--stdio"))) + +(defun kill-process () + (when (client) + (async-process:delete-process (client:client-process (client))))) + +(add-hook *exit-editor-hook* 'kill-process) + +(defun enable-copilot-p () + (config :copilot)) + +(defun enable-copilot () + (setf (config :copilot) t)) + + +;;; utils +(defvar *language-id-map* (make-hash-table :test 'eq)) + +(defun register-language-id (mode language-id) + (setf (gethash mode *language-id-map*) language-id)) + +(defun buffer-language-id (buffer) + (gethash (buffer-major-mode buffer) *language-id-map* "text")) + +(defun buffer-uri (buffer) + (lem-lsp-mode::buffer-uri buffer)) + +(defun buffer-version (buffer) + (buffer-value buffer 'version 0)) + +(defun (setf buffer-version) (version buffer) + (setf (buffer-value buffer 'version) version)) + +(defun buffer-last-version (buffer) + (buffer-value buffer 'last-version)) + +(defun (setf buffer-last-version) (last-version buffer) + (setf (buffer-value buffer 'last-version) last-version)) + +(defun buffer-update-version-p (buffer) + (not (equal (buffer-version buffer) + (buffer-last-version buffer)))) + +(defun buffer-completions-cache (buffer) + (buffer-value buffer 'completions-cache)) + +(defun (setf buffer-completions-cache) (completions-cache buffer) + (setf (buffer-value buffer 'completions-cache) completions-cache)) + +(defun buffer-showing-suggestions-p (buffer) + (buffer-value buffer 'showing-suggestions-p)) + +(defun (setf buffer-showing-suggestions-p) (showing-suggestions-p buffer) + (setf (buffer-value buffer 'showing-suggestions-p) showing-suggestions-p)) + +(defun point-to-lsp-position (point) + (hash "line" (1- (line-number-at-point point)) + "character" (point-charpos point))) + +(defun move-to-lsp-position (point position) + (move-to-line point (1+ (gethash "line" position))) + (line-offset point 0 (gethash "character" position))) + +(defun text-document-params (buffer) + (list :uri (buffer-uri buffer) + :language-id (buffer-language-id buffer) + :version (buffer-version buffer) + :text (buffer-text buffer))) + +(defun notify-text-document/did-open (buffer) + (apply #'client:text-document/did-open + (client) + (text-document-params buffer))) + +(defun notify-text-document/did-close (buffer) + (client:text-document/did-close (client) :uri (buffer-uri buffer))) + +(defun notify-text-document/did-focus (buffer) + (client:text-document/did-focus (client) :uri (buffer-uri buffer))) + +(defun notify-text-document/did-change (buffer content-changes) + (let ((version (incf (buffer-version buffer)))) + (client:text-document/did-change (client) + :uri (buffer-uri buffer) + :version version + :content-changes content-changes))) + +(defun initialize (client then) + (client:initialize client + :callback (lambda (response) + (send-event (lambda () + (funcall then response)))))) + +(defun set-editor-info (client then) + (client:set-editor-info client + :callback (lambda (response) + (send-event (lambda () + (funcall then response)))))) + + +;;; copilot-mode +(define-minor-mode copilot-mode + (:name "Copilot" + :keymap *copilot-mode-keymap* + :enable-hook 'copilot-mode-on + :disable-hook 'copilot-mode-off)) + +(define-key *copilot-mode-keymap* "M-n" 'copilot-next-suggestion) +(define-key *copilot-mode-keymap* "M-p" 'copilot-previous-suggestion) + +(defun setup-client-async (then) + (let ((client (client:run-client :process (run-process)))) + (client:connect client) + (initialize client + (lambda (response) + (declare (ignore response)) + (client:initialized client) + (set-editor-info client + (lambda (response) + (declare (ignore response)) + (setf *client* client) + (funcall then))))))) + +(defun copilot-mode-on () + (unless (installed-copilot-server-p) + (copilot-install-server) + (reset-buffers)) + (flet ((fn () + (add-hook (variable-value 'kill-buffer-hook :buffer (current-buffer)) 'on-kill-buffer) + (add-hook (variable-value 'before-change-functions :buffer (current-buffer)) 'on-before-change) + (add-hook *window-show-buffer-functions* 'on-window-show-buffer) + (add-hook *switch-to-window-hook* 'on-switch-to-window) + (add-hook *post-command-hook* 'on-post-command) + (notify-text-document/did-open (current-buffer)))) + (if (client) + (fn) + (setup-client-async #'fn)))) + +(defun copilot-mode-off () + (remove-hook (variable-value 'kill-buffer-hook :buffer (current-buffer)) 'on-kill-buffer) + (remove-hook (variable-value 'before-change-functions :buffer (current-buffer)) 'on-before-change) + (remove-hook *window-show-buffer-functions* 'on-window-show-buffer) + (remove-hook *switch-to-window-hook* 'on-switch-to-window)) + +(defun copilot-mode-p (buffer) + (mode-active-p buffer 'copilot-mode)) + +(defun on-kill-buffer (buffer) + (when (copilot-mode-p buffer) + (notify-text-document/did-close buffer))) + +(defun on-post-command () + (cancel-inline-completion)) + +(defun before-change-arg-to-content-change (point arg) + (etypecase arg + (string + (let ((position (point-to-lsp-position point))) + (hash "range" (hash "start" position + "end" position) + "text" arg))) + (integer + (with-point ((end point)) + (character-offset end arg) + (hash "range" (hash "start" (point-to-lsp-position point) + "end" (point-to-lsp-position end)) + "text" ""))))) + +(defvar *inhibit-did-change-notification* nil) + +(defun on-before-change (point arg) + (let ((buffer (point-buffer point))) + (when (and (copilot-mode-p buffer) + (not *inhibit-did-change-notification*)) + (notify-text-document/did-change + buffer + (vector (before-change-arg-to-content-change point arg)))))) + +(defun on-window-show-buffer (window) + (let ((buffer (window-buffer window))) + (when (copilot-mode-p buffer) + (notify-text-document/did-focus buffer)))) + +(defun on-switch-to-window (previous-window current-window) + (declare (ignore previous-window)) + (let ((buffer (window-buffer current-window))) + (when (copilot-mode-p buffer) + (notify-text-document/did-focus buffer)))) + +(defun reset-buffers () + (dolist (buffer (remove-if-not #'copilot-mode-p (buffer-list))) + (setf (buffer-version buffer) 0) + (notify-text-document/did-open buffer))) + +(define-command copilot-restart () () + (async-process:delete-process (client:client-process (client))) + (setup-client-async (lambda () + (reset-buffers) + (show-message "copilot restarted" + :style '(:gravity :center) + :timeout 3) + (redraw-display)))) + + +;;; complete +(defvar *inline-completion-request* nil) +(defvar *completion-canceled* nil) + +(defvar *copilot-completion-keymap* (make-keymap :name "Copilot Completion")) + +(define-key *copilot-completion-keymap* "Tab" 'copilot-accept-suggestion) +(define-key *copilot-completion-keymap* 'copilot-next-suggestion 'copilot-next-suggestion) +(define-key *copilot-completion-keymap* 'copilot-previous-suggestion 'copilot-previous-suggestion) + +(defun find-copilot-completion-command (key) + (lookup-keybind key + :keymaps (append (lem-core::all-keymaps) + (list *copilot-completion-keymap*)))) + +(defun search-preffix (str1 str2) + (loop :for i :from 0 + :for c1 :across str1 + :for c2 :across str2 + :while (char= c1 c2) + :finally (return i))) + +(defun replace-with-inline-completion (point item) + (let ((range (gethash "range" item))) + (with-point ((start point :left-inserting) + (end point :right-inserting)) + (move-to-lsp-position start (gethash "start" range)) + (move-to-lsp-position end (gethash "end" range)) + (let* ((insert-text (gethash "insertText" item)) + (text (points-to-string start end)) + (pos (search-preffix text insert-text))) + (character-offset start pos) + (delete-between-points start end) + (insert-string end + (subseq insert-text pos)))))) + +(defun preview-inline-completion-item (point item &key additional-text) + (let ((range (gethash "range" item))) + (with-point ((start point :left-inserting) + (end point :left-inserting)) + (move-to-lsp-position start (gethash "start" range)) + (move-to-lsp-position end (gethash "end" range)) + (let* ((insert-text (gethash "insertText" item)) + (text (points-to-string start end)) + (pos (search-preffix text insert-text))) + (character-offset start pos) + (delete-between-points start end) + (setf (buffer-showing-suggestions-p (point-buffer point)) t) + (insert-string end + (subseq insert-text pos) + :attribute 'suggestion-attribute) + (when additional-text + (insert-string end " ") + (insert-string end additional-text + :attribute 'cycling-attribute)))))) + +(defun unshow-inline-completion (point) + (when (buffer-showing-suggestions-p (point-buffer point)) + (setf (buffer-showing-suggestions-p (point-buffer point)) nil) + (let ((*inhibit-did-change-notification* t)) + (buffer-undo point)))) + +(defun elt-safety (items index) + (if (<= (length items) index) + (elt items (1- (length items))) + (elt items index))) + +(defun show-inline-completion (point items &key (index 0) cycling) + (let ((item (elt-safety items index)) + (buffer (point-buffer point)) + (*inhibit-did-change-notification* t)) + (lem-lsp-mode::reset-buffer-diagnostic buffer) + (buffer-undo-boundary buffer) + (save-excursion + (preview-inline-completion-item point + item + :additional-text (when cycling + (format nil + "[~D/~D]" + (1+ index) + (length items))))) + (loop :for v := (sit-for 10) + :while (eq v :timeout) + :finally (return-from show-inline-completion v)))) + +(defun prompt-inline-completion (point items &key (index 0) cycling) + (when items + (let ((buffer (point-buffer point)) + (key (show-inline-completion point items :index index :cycling cycling))) + (case (find-copilot-completion-command key) + (copilot-accept-suggestion + (read-key) + (unshow-inline-completion point) + (buffer-undo-boundary buffer) + (replace-with-inline-completion point (elt-safety items index)) + (redraw-display)) + (copilot-next-suggestion + (read-key) + (inline-completion point + :trigger-kind client:+trigger-kind.invoked+ + :index (mod (1+ index) (length items)) + :cycling t + :show-loading-spinner t)) + (copilot-previous-suggestion + (read-key) + (inline-completion point + :trigger-kind client:+trigger-kind.invoked+ + :index (mod (1- index) (length items)) + :cycling t + :show-loading-spinner t)) + (self-insert + (unshow-inline-completion point) + (buffer-undo-boundary buffer) + (self-insert 1 (insertion-key-p (read-key))) + (inline-completion point)) + (otherwise + (unshow-inline-completion point) + (error 'editor-abort :message nil)))))) + +(defun inline-completion (point &key (trigger-kind 2) (index 0) cycling show-loading-spinner) + (setf *completion-canceled* nil) + (let* ((buffer (point-buffer point)) + (spinner (when show-loading-spinner + (lem/loading-spinner:start-loading-spinner :line :point point))) + (request + (client:text-document/inline-completion + (client) + :callback (lambda (response) + (send-event (lambda () + (when spinner + (lem/loading-spinner:stop-loading-spinner spinner)) + (unshow-inline-completion point) + (unless *completion-canceled* + (prompt-inline-completion (buffer-point buffer) + (gethash "items" response) + :index index + :cycling cycling))))) + :error-callback (lambda (&rest args) + (declare (ignore args)) + (unshow-inline-completion point) + (send-event (lambda () + (when spinner + (lem/loading-spinner:stop-loading-spinner spinner))))) + :uri (buffer-uri buffer) + :position (point-to-lsp-position point) + :insert-spaces (if (variable-value 'indent-tabs-mode + :default buffer) + 'yason:true + 'yason:false) + :tab-size (variable-value 'tab-width :default buffer) + :trigger-kind trigger-kind))) + (setf *inline-completion-request* request))) + +(defun cancel-inline-completion () + (unshow-inline-completion (current-point)) + (when *inline-completion-request* + (client:$/cancel-request (client) (jsonrpc:request-id *inline-completion-request*)) + (setf *inline-completion-request* nil + *completion-canceled* t))) + +(define-command copilot-complete () () + (inline-completion (current-point))) + +(define-command copilot-accept-suggestion () () + ;; dummy command + ) + +(define-command copilot-next-suggestion () () + ;; dummy command + ) + +(define-command copilot-previous-suggestion () () + ;; dummy command + ) + +(defparameter *delay-complete* 1) +(defvar *complete-timer* nil) + +(defmethod execute :after ((mode copilot-mode) (command self-insert) argument) + (cond (*delay-complete* + (if *complete-timer* + (stop-timer *complete-timer*) + (setf *complete-timer* (make-idle-timer 'copilot-complete :name "Copilot Complete"))) + (start-timer *complete-timer* *delay-complete* :repeat nil)) + (t + (copilot-complete)))) diff --git a/extensions/copilot/install.lisp b/extensions/copilot/install.lisp new file mode 100644 index 000000000..69a5fab87 --- /dev/null +++ b/extensions/copilot/install.lisp @@ -0,0 +1,25 @@ +(uiop:define-package :lem-copilot/install + (:use :cl :lem) + (:local-nicknames (:client :lem-copilot/client) + (:logger :lem-copilot/logger) + (:copilot :lem-copilot)) + (:export :copilot-install-server)) +(in-package :lem-copilot/install) + +(define-command copilot-install-server () () + (let* ((buffer (make-buffer "*copilot-install-server*")) + (command (list "npm" + "-g" + "--prefix" + (namestring (copilot::copilot-root)) + "install" + "copilot-node-server@1.40.0"))) + (erase-buffer buffer) + (pop-to-buffer buffer) + (with-point ((point (buffer-point buffer) :left-inserting)) + (with-open-stream (output (make-editor-output-stream point)) + (format output "~{~A ~}~%" command) + (redraw-display) + (uiop:run-program command + :output output + :error-output output))))) diff --git a/extensions/copilot/languages.lisp b/extensions/copilot/languages.lisp new file mode 100644 index 000000000..cb78df8f4 --- /dev/null +++ b/extensions/copilot/languages.lisp @@ -0,0 +1,37 @@ +(defpackage :lem-copilot/languages + (:use :cl :lem) + (:local-nicknames (:copilot :lem-copilot)) + (:export :define-language)) +(in-package :lem-copilot/languages) + +(defun enable-copilot-mode () + (when (and (copilot::enable-copilot-p) + (not (buffer-temporary-p (current-buffer)))) + (copilot::copilot-mode t))) + +(defmacro define-language (mode (&key (language-id (alexandria:required-argument :language-id)))) + `(progn + (add-hook ,(mode-hook-variable mode) 'enable-copilot-mode) + (copilot::register-language-id ',mode ,language-id))) + +(define-language lem-js-mode:js-mode (:language-id "javascript")) +(define-language lem-rust-mode:rust-mode (:language-id "rust")) +(define-language lem-go-mode:go-mode (:language-id "go")) +(define-language lem-lisp-mode:lisp-mode (:language-id "lisp")) +(define-language lem-markdown-mode:markdown-mode (:language-id "markdown")) +(define-language lem-c-mode:c-mode (:language-id "c")) +(define-language lem-css-mode:css-mode (:language-id "css")) +(define-language lem-dart-mode:dart-mode (:language-id "dart")) +(define-language lem-json-mode:json-mode (:language-id "json")) +(define-language lem-lua-mode:lua-mode (:language-id "lua")) +(define-language lem-nim-mode:nim-mode (:language-id "nim")) +(define-language lem-ocaml-mode:ocaml-mode (:language-id "ocaml")) +(define-language lem-python-mode:python-mode (:language-id "python")) +(define-language lem-scala-mode:scala-mode (:language-id "scala")) +(define-language lem-scheme-mode:scheme-mode (:language-id "scheme")) +(define-language lem-sql-mode:sql-mode (:language-id "sql")) +(define-language lem-terraform-mode:terraform-mode (:language-id "hcl")) +(define-language lem-typescript-mode:typescript-mode (:language-id "typescript")) +(define-language lem-xml-mode:xml-mode (:language-id "xml")) +(define-language lem-yaml-mode:yaml-mode (:language-id "yaml")) +(define-language lem-swift-mode:swift-mode (:language-id "swift")) diff --git a/extensions/copilot/lem-copilot.asd b/extensions/copilot/lem-copilot.asd new file mode 100644 index 000000000..cdfc0b087 --- /dev/null +++ b/extensions/copilot/lem-copilot.asd @@ -0,0 +1,10 @@ +(defsystem "lem-copilot" + :depends-on ("lem" "lem-lsp-mode") + :components ((:file "utils") + (:file "logger") + (:file "client") + (:file "copilot") + (:file "install") + (:file "sign-in") + (:file "languages") + (:file "test-commands"))) diff --git a/extensions/copilot/logger.lisp b/extensions/copilot/logger.lisp new file mode 100644 index 000000000..7ff33d59d --- /dev/null +++ b/extensions/copilot/logger.lisp @@ -0,0 +1,19 @@ +(defpackage :lem-copilot/logger + (:use :cl) + (:local-nicknames (:ring :lem/common/ring)) + (:export :do-log + :write-log)) +(in-package :lem-copilot/logger) + +(defvar *logs* (ring:make-ring 1000)) +(defvar *log-lock* (bt2:make-lock :name "copilot log lock")) + +(defun do-log (output) + (bt2:with-lock-held (*log-lock*) + (ring:ring-push *logs* output))) + +(defun write-log (stream) + (bt2:with-lock-held (*log-lock*) + (loop :for i :downfrom (1- (ring:ring-length *logs*)) :to 0 + :for log := (ring:ring-ref *logs* i) + :do (write-line log stream)))) diff --git a/extensions/copilot/sign-in.lisp b/extensions/copilot/sign-in.lisp new file mode 100644 index 000000000..a999b8117 --- /dev/null +++ b/extensions/copilot/sign-in.lisp @@ -0,0 +1,77 @@ +(uiop:define-package :lem-copilot/sign-in + (:use :cl :lem) + (:local-nicknames (:lem-copilot :lem-copilot) + (:client :lem-copilot/client) + (:copilot :lem-copilot)) + (:export :copilot-signin)) +(in-package :lem-copilot/sign-in) + +(define-condition already-sign-in (editor-error) ()) + +(defvar *sign-in-message* nil) + +(defun make-verification-buffer (user-code verification-uri) + (let* ((buffer (make-buffer "*GitHub Copilot Verification*" :temporary t)) + (point (buffer-point buffer))) + (setf (variable-value 'line-wrap :buffer buffer) nil) + (erase-buffer buffer) + (insert-string point + (format nil + "Code: ~A (Copied to clipboard) ~%please paste it into your browser.~%~A~2%" + user-code + verification-uri)) + (insert-string point "Authenticate... (Close this window with Escape or C-g.)") + (buffer-start point) + buffer)) + +(defun start-sign-in (user-code verification-uri) + (setf *sign-in-message* (display-popup-message + (make-verification-buffer user-code verification-uri) + :style '(:gravity :center) + :timeout nil)) + (add-hook *editor-abort-hook* 'abort-sign-in)) + +(defun abort-sign-in () + (delete-sign-in-message) + (remove-hook *editor-abort-hook* 'abort-sign-in)) + +(defun delete-sign-in-message () + (when *sign-in-message* + (delete-popup-message *sign-in-message*) + (setf *sign-in-message* nil))) + +(define-command copilot-signin () () + (unless (copilot::installed-copilot-server-p) + (lem-copilot/install:copilot-install-server)) + (copilot::setup-client-async + (lambda () + (let* ((response (client:sign-in-initiate (copilot::client))) + (status (gethash "status" response)) + (user-code (gethash "userCode" response)) + (verification-uri (gethash "verificationUri" response)) + (user (gethash "user" response))) + (when (equal status "AlreadySignedIn") + (error 'already-sign-in :message (format nil "Already sign in as ~A" user))) + (copy-to-clipboard user-code) + (start-sign-in user-code verification-uri) + (open-external-file verification-uri) + (redraw-display) + (let ((finished nil)) + (client:sign-in-confirm + (copilot::client) + user-code + :callback (lambda (response) + (send-event (lambda () + (assert (equal "OK" (gethash "status" response))) + (show-message (format nil "Authenticated as ~A" (gethash "user" response)) + :style '(:gravity :center)) + (delete-sign-in-message) + (setf finished t) + (redraw-display))))) + (handler-bind ((editor-abort (lambda (c) + (declare (ignore c)) + (delete-sign-in-message)))) + + (loop :until finished + :do (sit-for 1))) + (copilot::enable-copilot)))))) diff --git a/extensions/copilot/test-commands.lisp b/extensions/copilot/test-commands.lisp new file mode 100644 index 000000000..afb2a243b --- /dev/null +++ b/extensions/copilot/test-commands.lisp @@ -0,0 +1,21 @@ +(defpackage :lem-copilot/test-commands + (:use :cl :lem :lem-copilot/utils) + (:local-nicknames (:client :lem-copilot/client) + (:logger :lem-copilot/logger) + (:copilot :lem-copilot))) +(in-package :lem-copilot/test-commands) + +(define-command test/copilot-document () () + (let ((response (client:request (copilot::client) + "testing/getDocument" + (hash "uri" (copilot::buffer-uri (current-buffer)))))) + (show-message (pretty-json response)) + (assert (equal (gethash "text" response) + (buffer-text (current-buffer)))))) + +(define-command test/copilot-log () () + (let* ((buffer (make-buffer "*copilot-log*" :enable-undo-p nil))) + (erase-buffer buffer) + (with-open-stream (stream (make-buffer-output-stream (buffer-point buffer))) + (logger:write-log stream)) + (pop-to-buffer buffer))) diff --git a/extensions/copilot/utils.lisp b/extensions/copilot/utils.lisp new file mode 100644 index 000000000..d7ea87743 --- /dev/null +++ b/extensions/copilot/utils.lisp @@ -0,0 +1,12 @@ +(defpackage :lem-copilot/utils + (:use :cl) + (:export :hash + :pretty-json)) +(in-package :lem-copilot/utils) + +(defun hash (&rest args) + (alexandria:plist-hash-table args :test 'equal)) + +(defun pretty-json (params) + (with-output-to-string (stream) + (yason:encode params (yason:make-json-output-stream stream)))) diff --git a/lib/encodings/8bit.lisp b/extensions/encodings/8bit.lisp similarity index 100% rename from lib/encodings/8bit.lisp rename to extensions/encodings/8bit.lisp diff --git a/lib/encodings/cp1250.table b/extensions/encodings/cp1250.table similarity index 100% rename from lib/encodings/cp1250.table rename to extensions/encodings/cp1250.table diff --git a/lib/encodings/cp1251.table b/extensions/encodings/cp1251.table similarity index 100% rename from lib/encodings/cp1251.table rename to extensions/encodings/cp1251.table diff --git a/lib/encodings/cp1253.table b/extensions/encodings/cp1253.table similarity index 100% rename from lib/encodings/cp1253.table rename to extensions/encodings/cp1253.table diff --git a/lib/encodings/cp1254.table b/extensions/encodings/cp1254.table similarity index 100% rename from lib/encodings/cp1254.table rename to extensions/encodings/cp1254.table diff --git a/lib/encodings/cp1255.table b/extensions/encodings/cp1255.table similarity index 100% rename from lib/encodings/cp1255.table rename to extensions/encodings/cp1255.table diff --git a/lib/encodings/cp1256.table b/extensions/encodings/cp1256.table similarity index 100% rename from lib/encodings/cp1256.table rename to extensions/encodings/cp1256.table diff --git a/lib/encodings/cp1257.table b/extensions/encodings/cp1257.table similarity index 100% rename from lib/encodings/cp1257.table rename to extensions/encodings/cp1257.table diff --git a/lib/encodings/cp866.table b/extensions/encodings/cp866.table similarity index 100% rename from lib/encodings/cp866.table rename to extensions/encodings/cp866.table diff --git a/lib/encodings/cp932.lisp b/extensions/encodings/cp932.lisp similarity index 100% rename from lib/encodings/cp932.lisp rename to extensions/encodings/cp932.lisp diff --git a/lib/encodings/cp932.table b/extensions/encodings/cp932.table similarity index 100% rename from lib/encodings/cp932.table rename to extensions/encodings/cp932.table diff --git a/lib/encodings/encodings-table/8bit.lisp b/extensions/encodings/encodings-table/8bit.lisp similarity index 100% rename from lib/encodings/encodings-table/8bit.lisp rename to extensions/encodings/encodings-table/8bit.lisp diff --git a/lib/encodings/encodings-table/euc.lisp b/extensions/encodings/encodings-table/euc.lisp similarity index 100% rename from lib/encodings/encodings-table/euc.lisp rename to extensions/encodings/encodings-table/euc.lisp diff --git a/lib/encodings/encodings-table/lem-encodings-table.asd b/extensions/encodings/encodings-table/lem-encodings-table.asd similarity index 100% rename from lib/encodings/encodings-table/lem-encodings-table.asd rename to extensions/encodings/encodings-table/lem-encodings-table.asd diff --git a/lib/encodings/encodings-table/main.lisp b/extensions/encodings/encodings-table/main.lisp similarity index 100% rename from lib/encodings/encodings-table/main.lisp rename to extensions/encodings/encodings-table/main.lisp diff --git a/lib/encodings/encodings-table/sjis.lisp b/extensions/encodings/encodings-table/sjis.lisp similarity index 100% rename from lib/encodings/encodings-table/sjis.lisp rename to extensions/encodings/encodings-table/sjis.lisp diff --git a/lib/encodings/euc-jp.lisp b/extensions/encodings/euc-jp.lisp similarity index 100% rename from lib/encodings/euc-jp.lisp rename to extensions/encodings/euc-jp.lisp diff --git a/lib/encodings/euc-jp.table b/extensions/encodings/euc-jp.table similarity index 100% rename from lib/encodings/euc-jp.table rename to extensions/encodings/euc-jp.table diff --git a/lib/encodings/gb2312.lisp b/extensions/encodings/gb2312.lisp similarity index 100% rename from lib/encodings/gb2312.lisp rename to extensions/encodings/gb2312.lisp diff --git a/lib/encodings/gb2312.table b/extensions/encodings/gb2312.table similarity index 100% rename from lib/encodings/gb2312.table rename to extensions/encodings/gb2312.table diff --git a/lib/encodings/iso-8859-1.lisp b/extensions/encodings/iso-8859-1.lisp similarity index 100% rename from lib/encodings/iso-8859-1.lisp rename to extensions/encodings/iso-8859-1.lisp diff --git a/lib/encodings/iso-8859-13.table b/extensions/encodings/iso-8859-13.table similarity index 100% rename from lib/encodings/iso-8859-13.table rename to extensions/encodings/iso-8859-13.table diff --git a/lib/encodings/iso-8859-2.table b/extensions/encodings/iso-8859-2.table similarity index 100% rename from lib/encodings/iso-8859-2.table rename to extensions/encodings/iso-8859-2.table diff --git a/lib/encodings/iso-8859-5.table b/extensions/encodings/iso-8859-5.table similarity index 100% rename from lib/encodings/iso-8859-5.table rename to extensions/encodings/iso-8859-5.table diff --git a/lib/encodings/iso-8859-6.table b/extensions/encodings/iso-8859-6.table similarity index 100% rename from lib/encodings/iso-8859-6.table rename to extensions/encodings/iso-8859-6.table diff --git a/lib/encodings/iso-8859-7.table b/extensions/encodings/iso-8859-7.table similarity index 100% rename from lib/encodings/iso-8859-7.table rename to extensions/encodings/iso-8859-7.table diff --git a/lib/encodings/iso-8859-8.table b/extensions/encodings/iso-8859-8.table similarity index 100% rename from lib/encodings/iso-8859-8.table rename to extensions/encodings/iso-8859-8.table diff --git a/lib/encodings/iso-8859-9.table b/extensions/encodings/iso-8859-9.table similarity index 100% rename from lib/encodings/iso-8859-9.table rename to extensions/encodings/iso-8859-9.table diff --git a/lib/encodings/koi8-r.table b/extensions/encodings/koi8-r.table similarity index 100% rename from lib/encodings/koi8-r.table rename to extensions/encodings/koi8-r.table diff --git a/lib/encodings/koi8-u.table b/extensions/encodings/koi8-u.table similarity index 100% rename from lib/encodings/koi8-u.table rename to extensions/encodings/koi8-u.table diff --git a/lib/encodings/lem-encodings.asd b/extensions/encodings/lem-encodings.asd similarity index 100% rename from lib/encodings/lem-encodings.asd rename to extensions/encodings/lem-encodings.asd diff --git a/lib/encodings/project.lisp b/extensions/encodings/project.lisp similarity index 100% rename from lib/encodings/project.lisp rename to extensions/encodings/project.lisp diff --git a/lib/encodings/table.lisp b/extensions/encodings/table.lisp similarity index 100% rename from lib/encodings/table.lisp rename to extensions/encodings/table.lisp diff --git a/lib/encodings/utf-16.lisp b/extensions/encodings/utf-16.lisp similarity index 100% rename from lib/encodings/utf-16.lisp rename to extensions/encodings/utf-16.lisp diff --git a/lib/encodings/utf-8.lisp b/extensions/encodings/utf-8.lisp similarity index 100% rename from lib/encodings/utf-8.lisp rename to extensions/encodings/utf-8.lisp diff --git a/extensions/erlang-mode/erlang-mode.lisp b/extensions/erlang-mode/erlang-mode.lisp index 8bbe29610..e05f2b908 100644 --- a/extensions/erlang-mode/erlang-mode.lisp +++ b/extensions/erlang-mode/erlang-mode.lisp @@ -352,7 +352,7 @@ "universaltime_to_posixtime" "unique_integer" "yield")) - + (defun tokens (boundary strings) (let ((alternation `(:alternation ,@(sort (copy-list strings) #'> :key #'length)))) @@ -360,6 +360,9 @@ `(:sequence ,boundary ,alternation ,boundary) alternation))) +(defun word-length-sort (&rest words) + (sort (copy-list words) #'> :key #'length)) + (defun wrap-symbol-names (&rest names) `(:sequence (:register @@ -386,10 +389,10 @@ (make-tm-match "[\\b]*[\?]+[\\w]+\\b" :name 'syntax-constant-attribute) (make-tm-line-comment-region "%") - (make-tm-string-region "\"") + (make-tm-string-region "\"") (make-tm-string-region "'") (make-tm-string-region "\"\"\"") - (make-tm-match (tokens :word-boundary + (make-tm-match (tokens :word-boundary (append *erlang-boolean-literals* *erlang-null-literal*)) :name 'syntax-constant-attribute) @@ -397,11 +400,11 @@ :name 'syntax-keyword-attribute) (make-tm-match `(:sequence - "-" ,(lem-lisp-mode/grammar::wrap-symbol-names + "-" ,(lem-lisp-mode/grammar::wrap-symbol-names "module" "behaviour" "behavior" "export" "export_type" "define" "record" "type" "include" "include_lib" "spec")) :captures (vector nil (make-tm-name 'syntax-keyword-attribute))) (make-tm-match "^[\n]*[a-z_]+" - :name 'syntax-function-name-attribute) + :name 'syntax-function-name-attribute) (make-tm-match (tokens :word-boundary (append '("when") *erlang-guards*)) :name 'syntax-guard-attribute) (make-tm-match (tokens :word-boundary *erlang-int-bifs*) @@ -445,7 +448,7 @@ (defun beginning-of-defun (point n) (loop :with regex = "^[a-z_\-]+\([A-Za-z\s\S_]*\)\s*->" - :repeat n + :repeat n :do (search-backward-regexp point regex))) (defun end-of-defun (point n) diff --git a/extensions/erlang-mode/lem-erlang-mode.asd b/extensions/erlang-mode/lem-erlang-mode.asd index 681a79462..fa06c2da6 100644 --- a/extensions/erlang-mode/lem-erlang-mode.asd +++ b/extensions/erlang-mode/lem-erlang-mode.asd @@ -1,10 +1,10 @@ (defsystem "lem-erlang-mode" :depends-on ("lem" - #+#.(cl:if (asdf:find-system :async-process cl:nil) '(and) '(or)) "lem-process") + #+#.(cl:if (asdf:find-system :async-process cl:nil) '(and) '(or)) "lem-process" + "lem-lisp-mode") :serial t :components ((:file "erlang-mode") (:file "lsp-config") #+#.(cl:if (asdf:find-system :async-process cl:nil) '(and) '(or)) (:file "run-erlang"))) - diff --git a/extensions/go-mode/go-mode.lisp b/extensions/go-mode/go-mode.lisp index c20a9e593..a5d0060fe 100644 --- a/extensions/go-mode/go-mode.lisp +++ b/extensions/go-mode/go-mode.lisp @@ -277,10 +277,10 @@ (defvar *fly-thread* nil) (defun run-flymake (fn) - (when (and *fly-thread* (bt:thread-alive-p *fly-thread*)) - (bt:destroy-thread *fly-thread*)) + (when (and *fly-thread* (bt2:thread-alive-p *fly-thread*)) + (bt2:destroy-thread *fly-thread*)) (setf *fly-thread* - (bt:make-thread fn :name "go-flymake"))) + (bt2:make-thread fn :name "go-flymake"))) (define-command goflymake (buffer) ((current-buffer)) diff --git a/extensions/js-mode/js-mode.lisp b/extensions/js-mode/js-mode.lisp index d1028f6d7..6ca8bbfe0 100644 --- a/extensions/js-mode/js-mode.lisp +++ b/extensions/js-mode/js-mode.lisp @@ -237,4 +237,4 @@ https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Lexical_grammar *prettier-options* (list (buffer-filename buf))))) -(define-file-type ("js" "jsx" "mjs") js-mode) +(define-file-type ("js" "jsx" "mjs" "cjs") js-mode) diff --git a/lib/language-client/client.lisp b/extensions/language-client/client.lisp similarity index 100% rename from lib/language-client/client.lisp rename to extensions/language-client/client.lisp diff --git a/lib/language-client/lem-language-client.asd b/extensions/language-client/lem-language-client.asd similarity index 100% rename from lib/language-client/lem-language-client.asd rename to extensions/language-client/lem-language-client.asd diff --git a/lib/language-client/request.lisp b/extensions/language-client/request.lisp similarity index 98% rename from lib/language-client/request.lisp rename to extensions/language-client/request.lisp index 5dee49bad..649c76f97 100644 --- a/lib/language-client/request.lisp +++ b/extensions/language-client/request.lisp @@ -14,11 +14,11 @@ (cl-package-locks:lock-package :lem-lsp-mode/request) (defvar *log-enable* t) -(defvar *log-mutex* (bt:make-lock)) +(defvar *log-mutex* (bt2:make-lock)) (defun do-log (string &rest args) (when *log-enable* - (bt:with-lock-held (*log-mutex*) + (bt2:with-lock-held (*log-mutex*) (let ((log-pathname (merge-pathnames "language-client.log" (lem:lem-logdir-pathname)))) (ensure-directories-exist log-pathname) (with-open-file (out log-pathname diff --git a/lib/language-server/cli.lisp b/extensions/language-server/cli.lisp similarity index 100% rename from lib/language-server/cli.lisp rename to extensions/language-server/cli.lisp diff --git a/lib/language-server/commands.lisp b/extensions/language-server/commands.lisp similarity index 100% rename from lib/language-server/commands.lisp rename to extensions/language-server/commands.lisp diff --git a/lib/language-server/config.lisp b/extensions/language-server/config.lisp similarity index 100% rename from lib/language-server/config.lisp rename to extensions/language-server/config.lisp diff --git a/lib/language-server/controller/commands.lisp b/extensions/language-server/controller/commands.lisp similarity index 100% rename from lib/language-server/controller/commands.lisp rename to extensions/language-server/controller/commands.lisp diff --git a/lib/language-server/controller/document-synchronization.lisp b/extensions/language-server/controller/document-synchronization.lisp similarity index 100% rename from lib/language-server/controller/document-synchronization.lisp rename to extensions/language-server/controller/document-synchronization.lisp diff --git a/lib/language-server/controller/language-features.lisp b/extensions/language-server/controller/language-features.lisp similarity index 100% rename from lib/language-server/controller/language-features.lisp rename to extensions/language-server/controller/language-features.lisp diff --git a/lib/language-server/controller/lifecycle.lisp b/extensions/language-server/controller/lifecycle.lisp similarity index 100% rename from lib/language-server/controller/lifecycle.lisp rename to extensions/language-server/controller/lifecycle.lisp diff --git a/lib/language-server/controller/window.lisp b/extensions/language-server/controller/window.lisp similarity index 100% rename from lib/language-server/controller/window.lisp rename to extensions/language-server/controller/window.lisp diff --git a/lib/language-server/controller/workspace.lisp b/extensions/language-server/controller/workspace.lisp similarity index 100% rename from lib/language-server/controller/workspace.lisp rename to extensions/language-server/controller/workspace.lisp diff --git a/lib/language-server/editor-utils.lisp b/extensions/language-server/editor-utils.lisp similarity index 100% rename from lib/language-server/editor-utils.lisp rename to extensions/language-server/editor-utils.lisp diff --git a/lib/language-server/eval.lisp b/extensions/language-server/eval.lisp similarity index 100% rename from lib/language-server/eval.lisp rename to extensions/language-server/eval.lisp diff --git a/lib/language-server/lem-language-server.asd b/extensions/language-server/lem-language-server.asd similarity index 97% rename from lib/language-server/lem-language-server.asd rename to extensions/language-server/lem-language-server.asd index 14880c090..8aeabcbe4 100644 --- a/lib/language-server/lem-language-server.asd +++ b/extensions/language-server/lem-language-server.asd @@ -12,7 +12,6 @@ "micros" "lem" "lem-lisp-syntax" - "lem-socket-utils" "lem-lsp-base") :serial t :components ((:file "micros-client") diff --git a/lib/language-server/method.lisp b/extensions/language-server/method.lisp similarity index 100% rename from lib/language-server/method.lisp rename to extensions/language-server/method.lisp diff --git a/lib/language-server/micros-client.lisp b/extensions/language-server/micros-client.lisp similarity index 99% rename from lib/language-server/micros-client.lisp rename to extensions/language-server/micros-client.lisp index a8fbc1c46..5d7f8ccec 100644 --- a/lib/language-server/micros-client.lisp +++ b/extensions/language-server/micros-client.lisp @@ -1,7 +1,7 @@ (defpackage :lem-language-server/micros-client (:nicknames :micros/client) (:use :cl) - (:import-from :lem-socket-utils + (:import-from :lem/common/socket :random-available-port) (:export :*write-string-function* :connection-port diff --git a/lib/language-server/package.lisp b/extensions/language-server/package.lisp similarity index 100% rename from lib/language-server/package.lisp rename to extensions/language-server/package.lisp diff --git a/lib/language-server/server.lisp b/extensions/language-server/server.lisp similarity index 100% rename from lib/language-server/server.lisp rename to extensions/language-server/server.lisp diff --git a/lib/language-server/text-document.lisp b/extensions/language-server/text-document.lisp similarity index 100% rename from lib/language-server/text-document.lisp rename to extensions/language-server/text-document.lisp diff --git a/extensions/legit/README.md b/extensions/legit/README.md index 5e5f54196..30c3f88c5 100644 --- a/extensions/legit/README.md +++ b/extensions/legit/README.md @@ -16,23 +16,10 @@ at your own risk. However it should run a few operations smoothly. - -## Load - -`legit` is built into Lem but it isn't loaded by default. To load it, open a Lisp REPL (`M-x start-lisp-repl`) or evaluate Lisp code (`M-:`) and type: - - (ql:quickload "lem/legit") - -Now you can start it with `C-x g` or `M-x legit-status`. - -![](lem-status.png) - -## Help - -Press `?` or `C-x ?` to call `legit-help`. - ## M-x legit-status +Bound to `C-x g` by default. + The status windows show us, on the left: - the current branch (or "trunk") @@ -46,6 +33,12 @@ and the window on the right shows us the file diffs or the commits' content. Refresh the status content with `g`. +![](lem-status.png) + +## Help + +Press `?` or `C-x ?` to call `legit-help`. + ## Navigation We can navigate inside legit windows with `n`, `p`, `M-n` and `M-p` (go to next/previous section). @@ -85,6 +78,22 @@ You can push to the current remote branch with `P p` and pull changes (fetch) wi Note: after pressing "P" or "F", you will not see an intermediate window giving you choices. Just press "P p" one after the other. +## Show commits log + +Press `l l` (lowercase "L", twice) to show the commits log in a dedicated buffer, with pagination. + +It defaults to showing the first 200 commits. Press `f` or `b` to see +the next or the previous page of the commits history. + +Press `F` and `B` to go to the last or first page of commits. + +Configuration: + +- you can set `lem/porcelain:*commits-log-page-size*` to another length (defaults to 200). + +Note that going to the last page with `B` might take a few seconds on large repositories. + + ## Interactive rebase You can start a Git interactive rebase. Place the cursor on a commit you want to rebase from, and press `r i`. @@ -166,41 +175,25 @@ Same with `*fossil-base-args*` and `*hg-base-arglist*`. - `*branch-sort-by*`: when listing branches, sort by this field name. Prefix with "-" to sort in descending order. Defaults to "-creatordate", to list the latest used branches first. - `*file-diff-args*`: defaults to `(list "diff" "--no-color")`. Arguments to display the file diffs. Will be surrounded by the git binary and the file path. For staged files, --cached is added by the command. -If a project is managed by more than one VCS, `legit` takes the first VCS defined in `*vcs-existence-order*`: +If a project is managed by more than one VCS, `legit` takes the first VCS defined in `lem/legit:*vcs-existence-order*`. You can change it like so: ~~~lisp -(defvar *vcs-existence-order* - '( - git-project-p - fossil-project-p - hg-project-p - )) +(setf lem/legit:*vcs-existence-order* '(:git :fossil :hg)) ~~~ -where these symbols are functions with no arguments that return two values: a truthy value if the current project is considered a Git/Fossil/Mercurial project, and a keyword representing the VCS: `:git`, `:fossil`, `:hg`. - -For example: - -~~~lisp -(defun hg-project-p () - "Return t if we find a .hg/ directory in the current directory (which should be the project root. Use `lem/legit::with-current-project`)." - (values (uiop:directory-exists-p ".hg") - :hg)) -~~~ - - -Variables and parameters in the `lem/legit` package. They might not be exported. - -- `*legit-verbose*`: If non nil, print some logs on standard output (terminal) and create the hunk patch file on disk at (lem-home)/lem-hunk-latest.patch. +You could also use a function to decide of your own order. It would +take no arguments and it must return one value: an instance of the VCS +object, such as `lem/porcelain/git::vcs-git`, if the current project is considered a +Git/Fossil/Mercurial project, and nil otherwise. -=> to help debugging +Variables and parameters for customization are defined in the `lem/legit` package. They might not be exported. see sources in `/extensions/legit/` ## Implementation details -Repository data is retrieved with calls to the VCS binary. We have a POC to read some data directly from the Git objects (and improve efficiency). +Repository data is retrieved with calls to the VCS binary, but currently only 2 or 3 calls are made for a legit status window, so it works fine with big repositories. We have a POC to read some data directly from the Git objects (and improve efficiency). The interactive rebase currently uses a Unix-only shell script. @@ -217,7 +210,6 @@ Please report any bug, and please let's discuss before you open a Github issue f and then: - visual submenu to pick subcommands -- view log - stage only selected region (more precise than hunks) - unstage/stage/discard multiple files - stashes diff --git a/extensions/legit/legit-commit.lisp b/extensions/legit/legit-commit.lisp index 06cf65bc8..05c2072e5 100644 --- a/extensions/legit/legit-commit.lisp +++ b/extensions/legit/legit-commit.lisp @@ -92,9 +92,9 @@ two ((str:blankp cleaned-message) (message "No commit message, do nothing.")) (t - (with-current-project () + (with-current-project (vcs) (run-function (lambda () - (lem/porcelain::commit cleaned-message)) + (lem/porcelain::commit vcs cleaned-message)) :message "commited") (kill-buffer "*legit-commit*") ;; come back on the status on the left: diff --git a/extensions/legit/legit-common.lisp b/extensions/legit/legit-common.lisp new file mode 100644 index 000000000..338503891 --- /dev/null +++ b/extensions/legit/legit-common.lisp @@ -0,0 +1,75 @@ + +(defpackage :lem/legit + (:use :cl + :lem) + (:export :legit-status + :*prompt-for-commit-abort-p* + :*ignore-all-space* + :*vcs-existence-order* + :*peek-legit-keymap* + :peek-legit-discard-file + :peek-legit-previous + :peek-legit-next) + (:documentation "Display version control data of the current project in an interactive two-panes window. + + This package in particular defines the right window of the legit interface and the user-level commands. + + Gets VCS data by calling lem/porcelain and asking lem/peek-legit to display data on the left window.")) + +(in-package :lem/legit) + +(defvar *default-vcs-existence-order* + (list :git :fossil :hg)) + +(defparameter *vcs-existence-order* *default-vcs-existence-order* + "The order in which to detect the VCS of the current project. + + List of keywords. Choices are: :git, :fossil and :hg.") + +(defvar *vcs-keyword-function-mapping* + (list :git #'lem/porcelain/git:git-project-p + :fossil #'lem/porcelain/fossil:fossil-project-p + :hg #'lem/porcelain/hg:hg-project-p + :mercurial #'lem/porcelain/hg:hg-project-p) + "A keyword to function mapping to help users configure their *vcs-existence-order*.") + +(defun vcs-project-p (&optional (vcs-order *vcs-existence-order*)) + "When this project is under a known version control system, returns a VCS object for the project. + Otherwise, returns nil." + ;; This doesn't return the 2 values :( + ;; (or (fossil-project-p) + ;; (git-project-p)) + (loop for choice in vcs-order + for fn = (if (keywordp choice) + (getf *vcs-keyword-function-mapping* choice) + choice) + for vcs = (funcall fn) + if vcs + return vcs)) + +(defun call-with-porcelain-error (function) + (handler-bind ((lem/porcelain:porcelain-error + (lambda (c) + (lem:editor-error (slot-value c 'lem/porcelain:message))))) + (funcall function))) + +(defmacro with-porcelain-error (&body body) + "Handle porcelain errors and turn them into a lem:editor-error." + ;; Doing this helps avoiding tight coupling between the porcelain package and Lem. + `(call-with-porcelain-error (lambda () ,@body))) + +(defun call-with-current-project (function) + (with-porcelain-error () + (let ((root (lem-core/commands/project:find-root (lem:buffer-directory)))) + (uiop:with-current-directory (root) + (let ((vcs (vcs-project-p *vcs-existence-order*))) + (if vcs + (funcall function vcs) + (lem:message "Not inside a version-controlled project?"))))))) + +(defmacro with-current-project ((vcs-bind) &body body) + "Execute body with the current working directory changed to the project's root, + find and `vcs-bind` as the VCS + + If no Git directory (or other supported VCS system) are found, message the user." + `(call-with-current-project (lambda (,vcs-bind) ,@body))) diff --git a/extensions/legit/legit-rebase.lisp b/extensions/legit/legit-rebase.lisp index cc33daa14..02765a2b3 100644 --- a/extensions/legit/legit-rebase.lisp +++ b/extensions/legit/legit-rebase.lisp @@ -72,21 +72,21 @@ and (define-key *legit-rebase-mode-keymap* "C-x ?" 'rebase-help) (define-command rebase-abort () () - (with-current-project () - (run-function #'lem/porcelain:rebase-abort) + (with-current-project (vcs) + (run-function (lambda () (lem/porcelain:rebase-abort vcs))) (when (get-buffer "git-rebase-todo") (kill-buffer "git-rebase-todo")) (message "rebase aborted."))) (define-command rebase-continue () () - (with-current-project () - (run-function #'lem/porcelain:rebase-continue) + (with-current-project (vcs) + (run-function (lambda () (lem/porcelain:rebase-continue vcs))) (when (get-buffer "git-rebase-todo") (kill-buffer "git-rebase-todo")))) (define-command rebase-skip () () - (with-current-project () - (run-function #'lem/porcelain:rebase-skip) + (with-current-project (vcs) + (run-function (lambda () (lem/porcelain:rebase-skip vcs))) (when (get-buffer "git-rebase-todo") (kill-buffer "git-rebase-todo")))) diff --git a/extensions/legit/legit.lisp b/extensions/legit/legit.lisp index ed4a86104..5541caa89 100644 --- a/extensions/legit/legit.lisp +++ b/extensions/legit/legit.lisp @@ -1,19 +1,15 @@ -(defpackage :lem/legit - (:use :cl - :lem) - (:export :legit-status - :*prompt-for-commit-abort-p* - :*ignore-all-space*) - (:documentation "Display version control data of the current project in an interactive two-panes window. - - This package in particular defines the right window of the legit interface and the user-level commands. - - Gets VCS data by calling lem/porcelain and asking lem/peek-legit to display data on the left window.")) (in-package :lem/legit) #| -An interactive interface to Git, with preliminary support for other version-control systems (Fossil, Mercurial). +Legit: an interactive interface to Git, with preliminary support for other version-control systems (Fossil, Mercurial). + +We display version control data of the current project in an interactive two-panes window. + +This file in particular defines the right window of the legit interface and the user-level commands. + +It gets VCS data by calling lem/porcelain and asking functions on peek-legit.lisp to display data on the left window. + Done: @@ -30,6 +26,7 @@ Done: - rebase interactively (see legit-rebase) - basic Fossil support (current branch, add change, commit) - basic Mercurial support +- show the commits log, with pagination Ongoing: @@ -43,9 +40,6 @@ Ongoing: |# -(defvar *legit-verbose* nil - "If non nil, print some logs on standard output (terminal) and create the hunk patch file on disk at (lem home)/lem-hunk-latest.patch.") - (defvar *ignore-all-space* nil "If non t, show all spaces in a diff. Spaces are ignored by default. Currently Git-only. Concretely, this calls Git with the -w option.") @@ -57,6 +51,11 @@ Currently Git-only. Concretely, this calls Git with the -w option.") :keymap *legit-diff-mode-keymap*) (setf (variable-value 'enable-syntax-highlight) t)) +(define-minor-mode legit-commits-log-mode + (:name "Log" + :keymap *legit-commits-log-keymap*) + (setf (not-switchable-buffer-p (current-buffer)) t)) + ;; git commands. ;; Some are defined on peek-legit too. (define-key *global-keymap* "C-x g" 'legit-status) @@ -66,48 +65,73 @@ Currently Git-only. Concretely, this calls Git with the -w option.") (define-key *legit-diff-mode-keymap* "p" 'legit-goto-previous-hunk) (define-key *legit-diff-mode-keymap* "c" 'legit-commit) -(define-key lem/peek-legit:*peek-legit-keymap* "c" 'legit-commit) +(define-key *peek-legit-keymap* "c" 'legit-commit) -(define-key lem/peek-legit:*peek-legit-keymap* "b b" 'legit-branch-checkout) +(define-key *peek-legit-keymap* "b b" 'legit-branch-checkout) (define-key *legit-diff-mode-keymap* "b b" 'legit-branch-checkout) -(define-key lem/peek-legit:*peek-legit-keymap* "b c" 'legit-branch-create) +(define-key *peek-legit-keymap* "b c" 'legit-branch-create) (define-key *legit-diff-mode-keymap* "b c" 'legit-branch-create) ;; push (define-key *legit-diff-mode-keymap* "P p" 'legit-push) -(define-key lem/peek-legit:*peek-legit-keymap* "P p" 'legit-push) +(define-key *peek-legit-keymap* "P p" 'legit-push) ;; pull -(define-key lem/peek-legit:*peek-legit-keymap* "F p" 'legit-pull) +(define-key *peek-legit-keymap* "F p" 'legit-pull) (define-key *legit-diff-mode-keymap* "F p" 'legit-pull) +;; commits log +(define-key *peek-legit-keymap* "l l" 'legit-commits-log) +(define-key *peek-legit-keymap* "l F" 'legit-commits-log-last-page) +(define-key *legit-diff-mode-keymap* "l l" 'legit-commits-log) + +;; only in commits log view +(define-key *legit-commits-log-keymap* "n" 'peek-legit-next) +(define-key *legit-commits-log-keymap* "p" 'peek-legit-previous) + +(define-key *legit-commits-log-keymap* "f" 'legit-commits-log-next-page) +(define-key *legit-commits-log-keymap* "b" 'legit-commits-log-previous-page) +(define-key *legit-commits-log-keymap* "F" 'legit-commits-log-last-page) +(define-key *legit-commits-log-keymap* "B" 'legit-commits-log-first-page) +(define-key *legit-commits-log-keymap* "q" 'legit-status) ;; could we save and re-display + ;; the status buffer? +(define-key *legit-commits-log-keymap* "Q" 'legit-quit) +(define-key *legit-commits-log-keymap* "?" 'legit-logs-help) + ;; rebase ;;; interactive -(define-key lem/peek-legit:*peek-legit-keymap* "r i" 'legit-rebase-interactive) -(define-key lem/peek-legit:*peek-legit-keymap* "r a" 'rebase-abort) -(define-key lem/peek-legit:*peek-legit-keymap* "r c" 'rebase-continue) -(define-key lem/peek-legit:*peek-legit-keymap* "r s" 'rebase-skip) +(define-key *peek-legit-keymap* "r i" 'legit-rebase-interactive) +(define-key *peek-legit-keymap* "r a" 'rebase-abort) +(define-key *peek-legit-keymap* "r c" 'rebase-continue) +(define-key *peek-legit-keymap* "r s" 'rebase-skip) ;; redraw everything: -(define-key lem/peek-legit:*peek-legit-keymap* "g" 'legit-status) +(define-key *peek-legit-keymap* "g" 'legit-status) ;; navigation (define-key *legit-diff-mode-keymap* "C-n" 'next-line) (define-key *legit-diff-mode-keymap* "C-p" 'previous-line) -(define-key lem/peek-legit:*peek-legit-keymap* "M-n" 'legit-next-header) -(define-key lem/peek-legit:*peek-legit-keymap* "M-p" 'legit-previous-header) +(define-key *peek-legit-keymap* "M-n" 'legit-next-header) +(define-key *peek-legit-keymap* "M-p" 'legit-previous-header) (define-key *legit-diff-mode-keymap* "Tab" 'next-window) +(define-key *legit-diff-mode-keymap* "Return" 'legit-jump-to-hunk) ;; help -(define-key lem/peek-legit:*peek-legit-keymap* "?" 'legit-help) -(define-key lem/peek-legit:*peek-legit-keymap* "C-x ?" 'legit-help) +(define-key *peek-legit-keymap* "?" 'legit-help) +(define-key *peek-legit-keymap* "C-x ?" 'legit-help) ;; quit (define-key *legit-diff-mode-keymap* "q" 'legit-quit) -(define-key lem/peek-legit:*peek-legit-keymap* "q" 'legit-quit) +(define-key *peek-legit-keymap* "q" 'legit-quit) (define-key *legit-diff-mode-keymap* "M-q" 'legit-quit) -(define-key lem/peek-legit:*peek-legit-keymap* "M-q" 'legit-quit) +(define-key *peek-legit-keymap* "M-q" 'legit-quit) (define-key *legit-diff-mode-keymap* "Escape" 'legit-quit) -(define-key lem/peek-legit:*peek-legit-keymap* "Escape" 'legit-quit) +(define-key *peek-legit-keymap* "Escape" 'legit-quit) (define-key *legit-diff-mode-keymap* "C-c C-k" 'legit-quit) -(define-key lem/peek-legit:*peek-legit-keymap* "C-c C-k" 'legit-quit) +(define-key *peek-legit-keymap* "C-c C-k" 'legit-quit) + + +(defmethod execute :after ((mode legit-commits-log-mode) command argument) + "After moving around the commit lines with n and p, show the commit diff on the right window." + (when (eq (current-window) *peek-window*) + (show-matched-line))) (defun pop-up-message (message) (with-pop-up-typeout-window (s (make-buffer "*legit status*") :erase t) @@ -116,37 +140,6 @@ Currently Git-only. Concretely, this calls Git with the -w option.") (defun last-character (s) (subseq s (- (length s) 2) (- (length s) 1))) -(defun call-with-porcelain-error (function) - (handler-bind ((lem/porcelain:porcelain-error - (lambda (c) - (lem:editor-error (slot-value c 'lem/porcelain::message))))) - (funcall function))) - -(defmacro with-porcelain-error (&body body) - "Handle porcelain errors and turn them into a lem:editor-error." - ;; This helps avoiding tight coupling. - `(call-with-porcelain-error (lambda () ,@body))) - -(defun call-with-current-project (function) - (with-porcelain-error () - (let ((root (lem-core/commands/project:find-root (buffer-directory)))) - (uiop:with-current-directory (root) - (multiple-value-bind (root vcs) - (lem/porcelain:vcs-project-p) - (if root - (let ((lem/porcelain:*vcs* vcs)) - (progn - (funcall function))) - (message "Not inside a version-controlled project?"))))))) - -(defmacro with-current-project (&body body) - "Execute body with the current working directory changed to the project's root, - find and set the VCS system for this operation. - - If no Git directory (or other supported VCS system) are found, message the user." - `(call-with-current-project (lambda () ,@body))) - - ;;; Git commands ;;; that operate on files. ;;; @@ -163,15 +156,43 @@ Currently Git-only. Concretely, this calls Git with the -w option.") (setf (buffer-read-only-p buffer) nil) (erase-buffer buffer) (move-to-line (buffer-point buffer) 1) - (insert-string (buffer-point buffer) diff) (change-buffer-mode buffer 'legit-diff-mode) + (insert-string (buffer-point buffer) diff) (setf (buffer-read-only-p buffer) t) (move-to-line (buffer-point buffer) 1))) -(defun make-diff-function (file &key cached) +(defun make-diff-function (file &key cached type) (lambda () - (with-current-project () - (show-diff (lem/porcelain:file-diff file :cached cached))))) + (with-current-project (vcs) + (cond + ((eq type :deleted) + (show-diff (format nil "File ~A has been deleted." file))) + ((and (not cached) (not type)) + (cond + ((uiop:file-exists-p file) + (handler-case + (show-diff (format nil "=== Untracked file: ~A ===~%~%~A" + file + (uiop:read-file-string file))) + (error () + (show-diff (format nil "=== Untracked file: ~A ===~%~%Unable to read file contents. It may be a binary file." + file))))) + ((uiop:directory-exists-p file) + (let* ((absolute-file (uiop:ensure-absolute-pathname file (uiop:getcwd))) + (contents (append (uiop:directory-files absolute-file) + (uiop:subdirectories absolute-file))) + (relative-paths (mapcar (lambda (path) + (format nil "~A~A" + file + (enough-namestring path absolute-file))) + contents))) + (show-diff (format nil "=== Untracked directory: ~A ===~%~%~{~A~%~}" + absolute-file + relative-paths)))) + (t + (show-diff (format nil "~A does not exist." file))))) + (t + (show-diff (lem/porcelain:file-diff vcs file :cached cached))))))) (defun make-visit-file-function (file) ;; note: the lambda inside the loop is not enough, it captures the last loop value. @@ -181,23 +202,23 @@ Currently Git-only. Concretely, this calls Git with the -w option.") ;; show commit. (defun make-show-commit-function (ref) (lambda () - (with-current-project () - (show-diff (lem/porcelain:show-commit-diff ref :ignore-all-space *ignore-all-space*))))) + (with-current-project (vcs) + (show-diff (lem/porcelain:show-commit-diff vcs ref :ignore-all-space *ignore-all-space*))))) ;; stage (defun make-stage-function (file) (lambda () - (with-current-project () - (lem/porcelain:stage file) + (with-current-project (vcs) + (lem/porcelain:stage vcs file) t))) ;; unstage (defun make-unstage-function (file &key already-unstaged) (lambda () - (with-current-project () + (with-current-project (vcs) (if already-unstaged (message "Already unstaged") - (lem/porcelain:unstage file))))) + (lem/porcelain:unstage vcs file))))) ;; discard an unstaged change. (defun make-discard-file-function (file &key is-staged) @@ -209,9 +230,9 @@ Currently Git-only. Concretely, this calls Git with the -w option.") (is-staged (message "Unstage the file first")) (t - (with-current-project () + (with-current-project (vcs) (when (prompt-for-y-or-n-p (format nil "Discard unstaged changes in ~a?" file)) - (lem/porcelain:discard-file file))))))) + (lem/porcelain:discard-file vcs file))))))) ;;; @@ -334,15 +355,15 @@ Currently Git-only. Concretely, this calls Git with the -w option.") (pop-up-message error-output)))))) (define-command legit-stage-hunk () () - (with-current-project () + (with-current-project (vcs) (run-function (lambda () - (lem/porcelain:apply-patch (%current-hunk))) + (lem/porcelain:apply-patch vcs (%current-hunk))) :message "Staged hunk"))) (define-command legit-unstage-hunk () () - (with-current-project () + (with-current-project (vcs) (run-function (lambda () - (lem/porcelain:apply-patch (%current-hunk) :reverse t)) + (lem/porcelain:apply-patch vcs (%current-hunk) :reverse t)) :message "Unstaged hunk"))) (define-command legit-goto-next-hunk () () @@ -368,6 +389,39 @@ Currently Git-only. Concretely, this calls Git with the -w option.") point start)))) +(define-command legit-jump-to-hunk () () + "Jump to the corresponding line in the source file for the current hunk." + (let ((first-line (with-point ((p (current-point))) + (buffer-start p) + (line-string p)))) + (when (str:starts-with-p "diff" first-line) + (let* ((hunk-text (%current-hunk)) + (lines (str:lines hunk-text)) + (file-line (find-if (lambda (line) (str:starts-with? "+++ b/" line)) lines)) + (hunk-header (find-if (lambda (line) (str:starts-with? "@@ " line)) lines))) + (if (and file-line hunk-header) + (let* ((relative-file (if (str:starts-with-p "diff --git" first-line) + (subseq file-line 6) ; Remove "+++ b/" prefix for Git + (cl-ppcre:register-groups-bind (file-path) + ("\\+\\+\\+ b/([^\\t]+)" file-line) + file-path))) ; For Mercurial (also has datetime) + (start-line (cl-ppcre:register-groups-bind (nil line) + ("@@ -(\\d+),\\d+ \\+(\\d+)" hunk-header) + line)) + (target-line (when start-line + (+ + (parse-integer start-line :junk-allowed t) + lem/porcelain:*diff-context-lines*)))) + (if (and relative-file target-line) + (with-current-project (vcs) + (declare (ignore vcs)) + (let ((absolute-file (merge-pathnames relative-file (uiop:getcwd)))) + (%legit-quit) + (find-file (namestring absolute-file)) + (goto-line target-line))) + (message "Could not determine file or line number"))) + (message "Could not parse hunk information")))))) + (defparameter *commit-buffer-message* "~%# Please enter the commit message for your changes.~%~ # Lines starting with '#' will be discarded, and an empty message does nothing.~%~ @@ -412,77 +466,96 @@ Currently Git-only. Concretely, this calls Git with the -w option.") (define-command legit-status () () - "Show changes and untracked files." - (with-current-project () + "Show changes, untracked files and latest commits in an interactive window." + (with-current-project (vcs) (multiple-value-bind (untracked-files unstaged-files staged-files) - (lem/porcelain:components) + (lem/porcelain:components vcs) ;; big try! It works \o/ - (lem/peek-legit:with-collecting-sources (collector :read-only nil) + (with-collecting-sources (collector :read-only nil + :minor-mode 'peek-legit-mode) + ;; (if we don't specify the minor-mode, the macro arguments's default value will not be found) + ;; ;; Header: current branch. - (lem/peek-legit:collector-insert - (format nil "Branch: ~a" (lem/porcelain:current-branch)) + (collector-insert + (format nil "Branch: ~a" (lem/porcelain:current-branch vcs)) :header t) - (lem/peek-legit:collector-insert "") + (collector-insert "") ;; Is a git rebase in progress? - (let ((rebase-status (lem/porcelain::rebase-in-progress))) + (let ((rebase-status (lem/porcelain::rebase-in-progress-p vcs))) (when (getf rebase-status :status) - (lem/peek-legit:collector-insert + (collector-insert (format nil "!rebase in progress: ~a onto ~a" (getf rebase-status :head-short-name) (getf rebase-status :onto-short-commit))) - (lem/peek-legit:collector-insert ""))) + (collector-insert ""))) ;; Untracked files. - (lem/peek-legit:collector-insert "Untracked files:" :header t) + (collector-insert (format nil "Untracked files (~a):" (length untracked-files)) :header t) (if untracked-files (loop :for file :in untracked-files - :do (lem/peek-legit:with-appending-source + :do (with-appending-source (point :move-function (make-diff-function file) :visit-file-function (make-visit-file-function file) :stage-function (make-stage-function file) :unstage-function (lambda () (message "File is not tracked, can't be unstaged."))) - (insert-string point file :attribute 'lem/peek-legit:filename-attribute :read-only t))) - (lem/peek-legit:collector-insert "")) + (insert-string point file :attribute 'filename-attribute :read-only t))) + (collector-insert "")) - (lem/peek-legit:collector-insert "") - ;; Unstaged changes. - (lem/peek-legit:collector-insert "Unstaged changes:" :header t) + ;; Unstaged changes + (collector-insert "") + (collector-insert (format nil "Unstaged changes (~a):" (length unstaged-files)) :header t) (if unstaged-files - (loop :for file :in unstaged-files - :do (lem/peek-legit:with-appending-source - (point :move-function (make-diff-function file) - :visit-file-function (make-visit-file-function file) - :stage-function (make-stage-function file) - :unstage-function (make-unstage-function file :already-unstaged t) - :discard-file-function (make-discard-file-function file)) - - (insert-string point file :attribute 'lem/peek-legit:filename-attribute :read-only t) - )) - (lem/peek-legit:collector-insert "")) - - (lem/peek-legit:collector-insert "") - (lem/peek-legit:collector-insert "Staged changes:" :header t) - - ;; Stages files. + (loop for file-info in unstaged-files + for file = (getf file-info :file) + for type = (getf file-info :type) + do (with-appending-source + (point :move-function (make-diff-function file :type type) + :visit-file-function (make-visit-file-function file) + :stage-function (make-stage-function file) + :unstage-function (make-unstage-function file :already-unstaged t) + :discard-file-function (make-discard-file-function file)) + (insert-string point + (format nil "~10a ~a" + (case type + (:modified "modified") + (:deleted "deleted") + (t "")) + file) + :attribute 'filename-attribute + :read-only t))) + (collector-insert "")) + + ;; Staged changes + (collector-insert "") + (collector-insert (format nil "Staged changes (~a):" (length staged-files)) :header t) (if staged-files - (loop :for file :in staged-files - :for i := 0 :then (incf i) - :do (lem/peek-legit:with-appending-source - (point :move-function (make-diff-function file :cached t) - :visit-file-function (make-visit-file-function file) - :stage-function (make-stage-function file) - :unstage-function (make-unstage-function file) - :discard-file-function (make-discard-file-function file :is-staged t)) - - (insert-string point file :attribute 'lem/peek-legit:filename-attribute :read-only t))) - (lem/peek-legit:collector-insert "")) + (loop for file-info in staged-files + for file = (getf file-info :file) + for type = (getf file-info :type) + do (with-appending-source + (point :move-function (make-diff-function file :cached t :type type) + :visit-file-function (make-visit-file-function file) + :stage-function (make-stage-function file) + :unstage-function (make-unstage-function file) + :discard-file-function (make-discard-file-function file :is-staged t)) + (insert-string point + (format nil "~10a ~a" + (case type + (:modified "modified") + (:added "created") + (:deleted "deleted") + (t "")) + file) + :attribute 'filename-attribute + :read-only t))) + (collector-insert "")) ;; Latest commits. - (lem/peek-legit:collector-insert "") - (lem/peek-legit:collector-insert "Latest commits:" :header t) - (let ((latest-commits (lem/porcelain:latest-commits))) + (collector-insert "") + (collector-insert "Latest commits:" :header t) + (let ((latest-commits (lem/porcelain:latest-commits vcs))) (if latest-commits (loop for commit in latest-commits for line = nil @@ -495,14 +568,14 @@ Currently Git-only. Concretely, this calls Git with the -w option.") else do (setf line commit) - do (lem/peek-legit:with-appending-source + do (with-appending-source (point :move-function (make-show-commit-function hash) :visit-file-function (lambda ()) :stage-function (lambda () ) :unstage-function (lambda () )) (with-point ((start point)) (when hash - (insert-string point hash :attribute 'lem/peek-legit:filename-attribute :read-only t)) + (insert-string point hash :attribute 'filename-attribute :read-only t)) (if message (insert-string point message) (insert-string point line)) @@ -510,64 +583,70 @@ Currently Git-only. Concretely, this calls Git with the -w option.") ;; Save the hash on this line for later use. (when hash (put-text-property start point :commit-hash hash))))) - (lem/peek-legit:collector-insert ""))) + (collector-insert ""))) - (add-hook (variable-value 'after-change-functions :buffer (lem/peek-legit:collector-buffer collector)) + (add-hook (variable-value 'after-change-functions :buffer (collector-buffer collector)) 'change-grep-buffer))))) -(defun prompt-for-branch (&key prompt initial-value) +(define-command legit () () + "Show changes, untracked files and latest commits in an interactive window. + + Calls M-x legit-status." + (legit-status)) + +(defun prompt-for-branch (vcs &key prompt initial-value) ;; only call from a command. - (let* ((current-branch (or initial-value (lem/porcelain:current-branch))) - (candidates (lem/porcelain:branches))) + (let* ((current-branch (or initial-value (lem/porcelain:current-branch vcs))) + (candidates (lem/porcelain:branches vcs))) (if candidates (prompt-for-string (or prompt "Branch: ") :initial-value current-branch :history-symbol '*legit-branches-history* :completion-function (lambda (x) (completion-strings x candidates)) :test-function (lambda (name) (member name candidates :test #'string=))) - (message "No branches. Not inside a git project?")))) + (message "No branches")))) (define-command legit-branch-checkout () () "Choose a branch to checkout." - (with-current-project () - (let ((branch (prompt-for-branch)) - (current-branch (lem/porcelain:current-branch))) + (with-current-project (vcs) + (let ((branch (prompt-for-branch vcs)) + (current-branch (lem/porcelain:current-branch vcs))) (when (equal branch current-branch) (show-message (format nil "Already on ~a" branch) :timeout 3) (return-from legit-branch-checkout)) (when branch (run-function (lambda () - (lem/porcelain:checkout branch)) + (lem/porcelain:checkout vcs branch)) :message (format nil "Checked out ~a" branch)) (legit-status))))) (define-command legit-branch-create () () "Create and checkout a new branch." - (with-current-project () + (with-current-project (vcs) (let ((new (prompt-for-string "New branch name: " :history-symbol '*new-branch-name-history*)) - (base (prompt-for-branch :prompt "Base branch: " :initial-value ""))) + (base (prompt-for-branch vcs :prompt "Base branch: " :initial-value ""))) (when (and new base) (run-function (lambda () - (lem/porcelain:checkout-create new base)) + (lem/porcelain:checkout-create vcs new base)) :message (format nil "Created ~a" new)) (legit-status))))) (define-command legit-pull () () "Pull changes, update HEAD." - (with-current-project () - (run-function #'lem/porcelain:pull))) + (with-current-project (vcs) + (run-function (lambda () (lem/porcelain:pull vcs))))) (define-command legit-push () () "Push changes to the current remote." - (with-current-project () - (run-function #'lem/porcelain:push))) + (with-current-project (vcs) + (run-function (lambda () (lem/porcelain:push vcs))))) (define-command legit-rebase-interactive () () "Rebase interactively, from the commit the point is on. Austostash pending changes, to enable the rebase and find the changes back afterwards." - (with-current-project () + (with-current-project (vcs) ;; Find the commit hash the point is on: mandatory. (let ((commit-hash (text-property-at (current-point) :commit-hash))) @@ -577,25 +656,101 @@ Currently Git-only. Concretely, this calls Git with the -w option.") (return-from legit-rebase-interactive)) (run-function (lambda () - (lem/porcelain::rebase-interactively :from commit-hash))) + (lem/porcelain::rebase-interactively vcs :from commit-hash))) (let ((buffer (find-file-buffer ".git/rebase-merge/git-rebase-todo"))) (when buffer - (lem/peek-legit::quit) + (%legit-quit) (switch-to-buffer buffer) (change-buffer-mode buffer 'legit-rebase-mode)))))) (define-command legit-next-header () () "Move point to the next header of this VCS window." - (lem/peek-legit:peek-legit-next-header)) + (peek-legit-next-header)) (define-command legit-previous-header () () "Move point to the previous header of this VCS window." - (lem/peek-legit:peek-legit-previous-header)) + (peek-legit-previous-header)) + +(define-command legit-commits-log () () + "List commits on a new buffer." + (with-current-project (vcs) + (display-commits-log vcs 0))) + +(defun display-commits-log (vcs offset) + "Display the commit lines on a dedicated legit buffer." + (let* ((commits (lem/porcelain:commits-log vcs :offset offset :limit lem/porcelain:*commits-log-page-size*))) + (with-collecting-sources (collector :buffer :commits-log + :minor-mode 'legit-commits-log-mode + :read-only nil) + (collector-insert + (format nil "Commits (~A):" offset) + :header t) + (if commits + (progn + (loop for commit in commits + for line = nil + for hash = nil + for message = nil + if (consp commit) + do (setf line (getf commit :line) + hash (getf commit :hash) + message (getf commit :message)) + else + do (setf line commit) + do (with-appending-source + (point :move-function (make-show-commit-function hash) + :visit-file-function (lambda ()) + :stage-function (lambda () ) + :unstage-function (lambda () )) + (with-point ((start point)) + (when hash + (insert-string point hash :attribute 'filename-attribute :read-only t)) + (if message + (insert-string point message) + (insert-string point line)) + (when hash + (put-text-property start point :commit-hash hash))))) + (setf (buffer-value (collector-buffer collector) 'commits-offset) offset)) + (collector-insert ""))))) + +(define-command legit-commits-log-next-page () () + "Show the next page of the commits log." + (with-current-project (vcs) + (let* ((buffer (current-buffer)) + (current-offset (or (buffer-value buffer 'commits-offset) 0)) + (new-offset (+ current-offset lem/porcelain:*commits-log-page-size*)) + (commits (lem/porcelain:commits-log vcs + :offset new-offset + :limit lem/porcelain:*commits-log-page-size*))) + (if commits + (display-commits-log vcs new-offset) + (message "No more commits to display."))))) + +(define-command legit-commits-log-previous-page () () + "Show the previous page of the commits log." + (with-current-project (vcs) + (let* ((buffer (current-buffer)) + (current-offset (or (buffer-value buffer 'commits-offset) 0)) + (new-offset (max 0 (- current-offset lem/porcelain:*commits-log-page-size*)))) + (display-commits-log vcs new-offset)))) + +(define-command legit-commits-log-first-page () () + "Go to the first page of the commit log." + (with-current-project (vcs) + (display-commits-log vcs 0))) + +(define-command legit-commits-log-last-page () () + "Go to the last page of the commit log." + (with-current-project (vcs) + (let* ((commits-per-page lem/porcelain:*commits-log-page-size*) + (last-page-offset (* (floor (/ (1- (lem/porcelain:commit-count vcs)) commits-per-page)) + commits-per-page))) + (display-commits-log vcs last-page-offset)))) (define-command legit-quit () () "Quit" - (lem/peek-legit:quit) + (%legit-quit) (ignore-errors (delete-buffer (get-buffer "*legit-diff*")) (delete-buffer (get-buffer "*legit-help*")))) @@ -611,6 +766,8 @@ Currently Git-only. Concretely, this calls Git with the -w option.") (format s "(c)ommit~&") (format s "(b)ranches-> checkout another (b)ranch.~&") (format s " -> (c)reate.~&") + (format s "(l)og-> (l) commits log~&") + (format s " -> (F) first page of the commits history~&") (format s "(F)etch, pull-> (p) from remote branch~&") (format s "(P)push -> (p) to remote branch~&") (format s "(r)ebase -> (i)nteractively from commit at point, (a)bort~&") @@ -620,5 +777,27 @@ Currently Git-only. Concretely, this calls Git with the -w option.") (format s "Change windows: Tab, C-x o, M-o~&") (format s "Quit: Escape, q, C-x 0.~&") (format s "~%") - (format s "Show this help: C-x ? or ?, M-x legit-help") + (format s "Show this help: C-x ? or ?, M-x legit-help~&") + (format s "~%") + (format s "You can customize:~&") + (format s "~%") + (format s "lem/porcelain:*nb-latest-commits* which defaults to 10~&") + (format s "(and more)~&") + )) + +(define-command legit-logs-help () () + "Help for the commits log view." + (with-pop-up-typeout-window (s (make-buffer "*legit-help*") :erase t) + (format s "In this commits log buffer, use:~&") + (format s "~%") + (format s "(f) forward page~&") + (format s "(b) backward one page~&") + (format s "(F) go to the last page of the commits history~&") + (format s "(B) come back to the first page of the commits history~&") + (format s "(q) come back to the legit status~&") + (format s "(Q) quit legit~&") + (format s "~%") + (format s "You can customize:~&") + (format s "~%") + (format s "lem/porcelain:*commits-log-page-size* which defaults to 200~&") )) diff --git a/extensions/legit/lem-legit.asd b/extensions/legit/lem-legit.asd new file mode 100644 index 000000000..2eb8f3ac9 --- /dev/null +++ b/extensions/legit/lem-legit.asd @@ -0,0 +1,15 @@ +(defsystem "lem-legit" + :serial t + :depends-on ("lem" "lem-patch-mode" "lem-yaml-mode" "lem-markdown-mode") + :components ((:module "./" + :components ((:file "porcelain") + (:file "porcelain-git") + (:file "porcelain-hg") + (:file "porcelain-fossil") + (:file "legit-common") + (:file "peek-legit") + (:file "legit") + (:file "legit-rebase") + (:file "legit-commit"))) + (:module "scripts" + :components ((:static-file "dumbrebaseeditor.sh"))))) diff --git a/extensions/legit/peek-legit.lisp b/extensions/legit/peek-legit.lisp index 9f9bc85f5..3920e4695 100644 --- a/extensions/legit/peek-legit.lisp +++ b/extensions/legit/peek-legit.lisp @@ -1,37 +1,19 @@ -(defpackage :lem/peek-legit - (:use :cl :lem) - (:export :*peek-legit-keymap* - :collector-buffer - :collector-insert - :filename-attribute - :get-move-function - :highlight-matched-line - :position-attribute - :show-matched-line - :with-appending-source - :with-collecting-sources - :with-insert - :peek-legit-next-header - :peek-legit-next - :peek-legit-select - :peek-legit-previous-header - :peek-legit-previous - :peek-legit-stage-file - :peek-legit-unstage-file - :quit) - (:documentation "Defines the left window of the legit interface. - - Writes on the window the VCS components that are sent by the :legit package: untracked files, changes, staged changes, latest commits… They are displayed with custom attributes (read-only colors…) and text properties (on this line, the function to call on Enter is this lambda function…). - Cursor mouvements and keybindings send changes to the right window.")) - #| +peek-legit defines the left window of the legit interface. + +It writes on the window the VCS components: untracked files, changes, staged changes, latest commits… They are displayed with custom attributes (read-only colors…) and text properties (on this line, the function to call on Enter is this lambda function…). + +Cursor mouvements and keybindings send changes to the right window. + + Notes: - if names don't conflict, use a :keyword for text properties, not a 'symbol (:commit-hash vs 'commit-hash). Keywords are easier to manipulate from another source file (no home package). +- the dichotomoy peek-legit / legit originally follows grep-mode. |# -(in-package :lem/peek-legit) +(in-package :lem/legit) (define-minor-mode peek-legit-mode @@ -212,7 +194,7 @@ Notes: :use-border t))) (list peek-window source-window))) -(defun display (collector) +(defun display (collector &key (minor-mode 'peek-legit-mode)) (when (boundp '*peek-window*) (delete-window *peek-window*)) (when (boundp '*source-window*) @@ -228,32 +210,61 @@ Notes: (setf *source-window* source-window) (setf (current-window) peek-window) - (peek-legit-mode t) + + (funcall minor-mode t) + ;; aka: + ;; (peek-legit-mode t) (start-move-point (buffer-point (collector-buffer collector))) (show-matched-line))) -(defun make-peek-legit-buffer () - (let ((buffer (make-buffer "*peek-legit*" +(defun make-peek-legit-buffer (&key (name "*peek-legit*")) + "Get or create a buffer of name NAME. By default, use a `*peek-legit*' buffer. + This is where we will display legit information (status…)." + (let ((buffer (make-buffer name :temporary t :enable-undo-p t :directory (uiop:getcwd)))) (setf (variable-value 'line-wrap :buffer buffer) nil) buffer)) -(defun call-with-collecting-sources (function &key read-only) - (let* ((*collector* (make-instance 'collector :buffer (make-peek-legit-buffer))) +(defun call-with-collecting-sources (function &key read-only buffer (minor-mode 'peek-legit-mode)) + "Initialize variables to display things on a legit buffer. + + BUFFER: either :status or :commits-log. + READ-ONLY: boolean. + MINOR-MODE: the minor mode to activate after we displayed things in the buffer. Defaults to the main peek-legit-mode. The mode is activated with: + + (peek-legit-mode t) + or + (funcall minor-mode t)" + (let* ((*collector* (make-instance 'collector + :buffer + (make-peek-legit-buffer + :name + (case buffer + (:status "*peek-legit*") + (:commits-log "*legit-commits-log*") + (t (error "Unknown buffer name to display legit data: ~a" buffer)))))) (point (buffer-point (collector-buffer *collector*)))) (declare (ignorable point)) (funcall function *collector*) (when read-only (setf (buffer-read-only-p (collector-buffer *collector*)) t)) - (display *collector*))) + (display *collector* :minor-mode minor-mode))) -(defmacro with-collecting-sources ((collector &key (read-only t)) &body body) +(defmacro with-collecting-sources ((collector &key (buffer :status) + (read-only t) + (minor-mode 'peek-legit-mode)) + &body body) + "Top-level macro that prepares a buffer to print stuff on and activates a minor-mode. + + Then see `with-appending-source' and `collector-insert'." `(call-with-collecting-sources (lambda (,collector) (declare (ignorable ,collector)) ,@body) + :buffer ,buffer + :minor-mode ,minor-mode :read-only ,read-only)) (defun call-with-appending-source (insert-function @@ -279,6 +290,7 @@ Notes: stage-function unstage-function discard-file-function) &body body) + "Macro to use inside `with-collecting-sources' to print stuff." `(call-with-appending-source (lambda (,point) ,@body) ,move-function ,visit-file-function @@ -322,6 +334,12 @@ Notes: (window-see (current-window)))))) (defmethod execute :after ((mode peek-legit-mode) command argument) + "After a command is run in this mode, apply an effect. + + In the case of `peek-legit-mode', it is run after `peek-legit-next', + in order to show the file content on the right window. + + The method is to subclass for all legit modes." (when (eq (current-window) *peek-window*) (show-matched-line))) @@ -334,19 +352,20 @@ Notes: (define-command peek-legit-select () () - (alexandria:when-let ((file (get-matched-file))) - (quit) - (alexandria:if-let - ((buffer (or (and (uiop:file-exists-p file) - (find-file-buffer file)) - (find-file-buffer - (merge-pathnames - (lem-core/commands/project:find-root (buffer-filename)) - file))))) - (switch-to-buffer buffer) - (editor-error "File ~a doesn't exist." file)))) + (alexandria:when-let ((path (get-matched-file))) + (%legit-quit) + (with-current-project (vcs) + (declare (ignore vcs)) + (let ((full-path (merge-pathnames path (uiop:getcwd)))) + (if (or (uiop:file-exists-p full-path) + (uiop:directory-exists-p full-path)) + (find-file (namestring full-path)) + (editor-error "Path ~a doesn't exist." full-path)))))) (define-command peek-legit-next () () + "Find the next line with a :move-marker text property. + + After finding it, our :after method of `execute' is run, to apply an effect, showing the new diff on the right." (next-move-point (current-point))) (define-command peek-legit-next-header () () @@ -363,7 +382,7 @@ Notes: (point (funcall stage))) ;; Update the buffer, to see that a staged file goes to the staged section. ;; This calls git again and refreshes everything. - (uiop:symbol-call :lem/legit :legit-status) + (lem/legit:legit-status) point)) (define-command peek-legit-unstage-file () () @@ -371,18 +390,19 @@ Notes: (point (funcall unstage))) ;; Update the buffer, to see that a staged file goes to the staged section. ;; This calls git again and refreshes everything. - (uiop:symbol-call :lem/legit :legit-status) - point)) + (lem/legit:legit-status) + point)) + (define-command peek-legit-discard-file () () "Discard the changes in this file. The file should not be stage." (alexandria:when-let* ((fn (get-discard-file-function (buffer-point (window-buffer *peek-window*)))) (point (funcall fn))) ;; Update the buffer. ;; This calls git again and refreshes everything. - (uiop:symbol-call :lem/legit :legit-status) + (lem/legit:legit-status) point)) -(defun quit () +(defun %legit-quit () "Delete the two side windows." (setf (current-window) *parent-window*) (start-timer diff --git a/extensions/legit/porcelain-fossil.lisp b/extensions/legit/porcelain-fossil.lisp new file mode 100644 index 000000000..5f754b1bd --- /dev/null +++ b/extensions/legit/porcelain-fossil.lisp @@ -0,0 +1,157 @@ +(uiop:define-package :lem/porcelain/fossil + (:use :cl + ;; we import all the classes and methods defined in the main porcelain: + :lem/porcelain) + (:shadow :push) + (:import-from :trivial-types + :proper-list) + (:export :fossil-project-p) + (:documentation "Implements the porcelain interface for fossil-based repos.")) + +(in-package :lem/porcelain/fossil) + +(declaim (type (proper-list string) *fossil-base-args*)) +(defvar *fossil-base-args* (list "fossil") + "The fossil program, to be appended command-line options.") + +;; VCS implementation for fossil +(defclass vcs-fossil (vcs-project) + () + (:default-initargs + :name "fossil")) + +(defvar *projects-mapping* (make-hash-table :test #'equal) + "Map a VCS project root to its VCS object. + + We need a single VCS object to retain data. + + Note: no special data is saved for Fossil projects yet.") + +(defun get-or-create-project () + "If a vcs-fossil object exists for the current project root (as of + the CWD, set by `with-current-project', return it. Otherwise, create a + new `vcs-fossil' object instance." + (let* ((root (uiop:getcwd)) ;; CWD set by with-current-project. + (existing (gethash root *projects-mapping*))) + (or existing + (setf (gethash root *projects-mapping*) + (make-instance 'vcs-fossil))))) + +(defun fossil-project-p () + "Return t if we either find a .fslckout file in the current directory (should be the project root) or a file with a .fossil extension." + (cond + ((uiop:file-exists-p ".fslckout") + (make-instance 'vcs-fossil)) + (t + ;; Maybe do we need "fossil open" here. + (loop for file in (uiop:directory-files "./") + if (equal "fossil" (pathname-type file)) + return (get-or-create-project))))) + +(defun run-fossil (arglist) + "Run the fossil command with these options. + + arglist: a list of CLI options and arguments to append to the base fossil program. + See `*fossil-base-args*`." + (uiop:run-program (concatenate 'list + *fossil-base-args* + (uiop:ensure-list arglist)) + :output :string + :error-output :string + :ignore-error-status t)) + +(defun porcelain () + "Get changes." + (multiple-value-bind (out error code) + (run-fossil "changes") + (cond + ((not (zerop code)) + (porcelain-error (str:join #\newline (list out error)))) + (t + (values out error))))) + +(defmethod components ((vcs vcs-fossil)) + "Return values: + - untracked files + - list of ADDED files + - modified files" + (loop for line in (str:lines (porcelain)) + for parts = (str:words line) + for status = (first parts) + for file = (second parts) + if (equal "ADDED" status) + collect (list :file file :type :added) into modified-staged-files + if (equal "EDITED" status) + collect (list :file file :type :modified) into modified-staged-files + if (equal "DELETED" status) + collect (list :file file :type :deleted) into modified-staged-files + if (equal "UNKNOWN" status) + collect file into untracked-files + finally (return (values untracked-files + nil + modified-staged-files)))) + +(defmethod file-diff ((vcs vcs-fossil) file &key cached) + (declare (ignorable cached)) + (run-fossil (list "diff" file))) + + +(defmethod show-commit-diff ((vcs vcs-fossil) ref &key ignore-all-space) + (declare (ignore vcs) + (ignore ref) + (ignore ignore-all-space)) + nil) + + +(defmethod commit ((vcs vcs-fossil) message) + (run-fossil (list "commit" "-m" message))) + +(defmethod branches ((vcs vcs-fossil) &key sort-by) + (declare (ignore sort-by)) + (porcelain-error "not implemented")) + +(defmethod current-branch ((vcs vcs-fossil)) + ;; strip out "* " from "* trunk" + (str:trim + (subseq (run-fossil "branch") 2))) + +(defun %not-fossil-commit-line (line) + (str:starts-with-p "+++ no more data" line)) + +(defun fossil-commit-count () + ;; Not really tested in Lem. + (length + ;; Does the timeline command always end with "+++ no more data (1) +++" ? + (remove-if #'%not-fossil-commit-line + (str:lines + (run-fossil (list "timeline" "--oneline")))))) + +(defmethod commit-count ((vcs vcs-fossil)) + (fossil-commit-count)) + +(defmethod apply-patch ((vcs vcs-fossil) diff &key reverse) + "Needs fossil at least > 2.10. Version 2.22 works." + (declare (ignorable diff reverse)) + ;; mmh… it wants a binary patch. + (values nil "fossil patch is not supported." 1)) + +(defmethod latest-commits ((vcs vcs-fossil) &key (n lem/porcelain:*nb-latest-commits*) (hash-length 8) (offset 0)) + (declare (ignorable n hash-length offset)) + ;; return bare result. + (str:lines (run-fossil "timeline"))) + +(defmethod commits-log ((vcs vcs-fossil) &key (offset 0) limit (hash-length 8)) + (declare (ignorable hash-length)) + (let* ((commits (latest-commits vcs)) + (total-commits (length commits)) + (end (when limit (min total-commits (+ offset limit))))) + (if (>= offset total-commits) + nil + (subseq commits offset end)))) + +(defmethod stage ((vcs vcs-fossil) file) + (run-fossil (list "add" file))) + +(defmethod rebase-interactively ((vcs vcs-fossil) &key from) + (declare (ignore from)) + (porcelain-error "No interactive rebase for Fossil.")) diff --git a/extensions/legit/porcelain-git.lisp b/extensions/legit/porcelain-git.lisp new file mode 100644 index 000000000..f290b3725 --- /dev/null +++ b/extensions/legit/porcelain-git.lisp @@ -0,0 +1,439 @@ +(uiop:define-package :lem/porcelain/git + (:use :cl + ;; let's import all the classes and methods defined in the main porcelain: + :lem/porcelain) + ;; beware, we still shadow cl:push to have a "push" method: + (:shadow :push) + (:import-from :trivial-types + :proper-list) + (:export :git-project-p) + (:documentation "Implements the porcelain interface for git-based repos.")) + +(in-package :lem/porcelain/git) + +(declaim (type (proper-list string) *git-base-arglist*)) +(defvar *git-base-arglist* (list "git") + "The git program, to be appended command-line options.") + +(defvar *branch-sort-by* "-creatordate" + "When listing branches, sort by this field name. + Prefix with \"-\" to sort in descending order. + + Defaults to \"-creatordate\", to list the latest used branches first.") + +(defvar *file-diff-args* (list "diff" "--no-color") + "Arguments to display the file diffs. + Will be surrounded by the git binary and the file path. + + For staged files, --cached is added by the command.") + +(defvar *verbose* nil) + +;; VCS implementation for git +(defclass vcs-git (vcs-project) + ((rebase-pid :initform nil + :accessor rebase-pid + :type (or null string) + :documentation "When a rebase is started, save its PID.")) + (:default-initargs + :name "git")) + + +(defvar *projects-mapping* (make-hash-table :test #'equal) + "Map a VCS project root to its VCS object. + + We need a single VCS object to retain data, such as the ongoing rebase PID.") + +(defun get-or-create-project () + "If a vcs-git object exists for the current project root (as of + the CWD, set by `with-current-project', return it. Otherwise, create a + new `vcs-git' object instance. + + This is important to save the current rebase PID." + (let* ((root (uiop:getcwd)) ;; CWD set by with-current-project. + (existing (gethash root *projects-mapping*))) + (or existing + (setf (gethash root *projects-mapping*) + (make-instance 'vcs-git))))) + +(defun git-project-p () + "When we find a .git/ directory in the current directory (which should be the project root. Use `lem/porcelain:with-current-project`), + return a VCS object representing the repo: otherwise, return nil" + (when (uiop:directory-exists-p ".git") + (get-or-create-project))) + +(defun run-git (arglist) + "Run the git command with these options (list). + Return standard and error output as strings. + + Don't signal an error if the process doesn't exit successfully + (but return the error message, so it can be displayed by the caller). + However, if uiop:run-program fails to run the command, the interactive debugger + is invoked (and shown correctly in Lem)." + (uiop:run-program (concatenate 'list + *git-base-arglist* + (uiop:ensure-list arglist)) + :output :string + :error-output :string + :ignore-error-status t)) + +(defun porcelain () + "Return the git status: for each file in the project, the `git status --porcelain` command +allows to learn about the file state: modified, deleted, ignored… " + (run-git (list "status" "--porcelain=v1"))) + +(defmethod components ((vcs vcs-git)) + "Return 3 values: + - untracked files + - modified and unstaged files + - modified and staged files + + Git manual: + + In the short-format, the status of each path is shown as one of these forms + XY PATH + XY ORIG_PATH -> PATH + For paths with merge conflicts, X and Y show the modification states of each side of the + merge. For paths that do not have merge conflicts, X shows the status of the index, and Y + shows the status of the work tree. For untracked paths, XY are ??. Other status codes can + be interpreted as follows: + • ' ' = unmodified + • M = modified + • A = added + • D = deleted + • R = renamed + • C = copied + • U = updated but unmerged" + (loop for line in (str:lines (porcelain)) + for file = (subseq line 3) + for status = (subseq line 0 2) + unless (str:blankp line) + if (equal (elt status 0) #\M) + collect (list :file file :type :modified) into modified-staged-files + if (equal (elt status 0) #\A) + collect (list :file file :type :added) into modified-staged-files + if (equal (elt status 0) #\D) + collect (list :file file :type :deleted) into modified-staged-files + if (equal (elt status 1) #\M) + collect (list :file file :type :modified) into modified-unstaged-files + if (equal (elt status 1) #\D) + collect (list :file file :type :deleted) into modified-unstaged-files + if (str:starts-with-p "??" status) + collect file into untracked-files + finally (return (values untracked-files + modified-unstaged-files + modified-staged-files)))) + +(defmethod checkout ((vcs vcs-git) branch) + (run-git (list "checkout" branch))) + +(defmethod file-diff ((vcs vcs-git) file &key cached) + ;; --cached is a synonym for --staged. + ;; So it is set only for staged files. From git-components: the 3rd value, modified and staged files. + (run-git + (concatenate 'list + *file-diff-args* + (list (format nil "-U~D" lem/porcelain:*diff-context-lines*)) + (if cached '("--cached")) + (list file)))) + +(defmethod show-commit-diff ((vcs vcs-git) ref &key ignore-all-space) + (let ((options '())) + (when ignore-all-space + (cl:push "-w" options)) + (run-git `("show" ,@options ,ref)))) + +(defmethod commit ((vcs vcs-git) message) + (run-git (list "commit" "-m" message))) + +(defun list-branches (&key (sort-by *branch-sort-by*)) + "Return: list of branches, raw output. + Each element starts with two meaningful characters, such as \"* \" for the current branch." + (str:lines + (run-git (list "branch" + "--list" + "--no-color" + (format nil "--sort=~a" sort-by))))) + +(defmethod branches ((vcs vcs-git) &key (sort-by *branch-sort-by*)) + (loop for branch in (list-branches :sort-by sort-by) + collect (subseq branch 2 (length branch)))) + +(defmethod checkout-create ((vcs vcs-git) new start) + (run-git (list "checkout" "-b" new start))) + +(defmethod pull ((vcs vcs-git)) + ;; xxx: recurse submodules, etc. + (run-git (list "pull" "HEAD"))) + +(defmethod push ((vcs vcs-git)) + (run-git (list "push"))) + +(defmethod current-branch ((vcs vcs-git)) + (let ((branches (list-branches :sort-by "-creatordate"))) + (loop for branch in branches + if (str:starts-with-p "*" branch) + return (subseq branch 2)))) + +(defmethod rebase-in-progress-p ((vcs vcs-git)) + (when (uiop:directory-exists-p ".git/rebase-merge/") + (let ((head (str:trim (str:from-file ".git/rebase-merge/head-name"))) + (onto (str:trim (str:from-file ".git/rebase-merge/onto")))) + (list :status t + :head-name head + :head-short-name (or (third (str:split "/" head)) + head) + :onto onto + :onto-short-commit (str:shorten 8 onto :ellipsis ""))))) + +(defun %git-list-latest-commits (&optional (n lem/porcelain:*nb-latest-commits*)) + (when (plusp n) + (str:lines + (run-git (list "log" "--pretty=oneline" "-n" (princ-to-string n)))))) + +(defmethod latest-commits ((vcs vcs-git) &key (n lem/porcelain:*commits-log-page-size*) (hash-length 8) (offset 0)) + (let* ((n-arg (when n (list "-n" (princ-to-string n)))) + (lines (str:lines + (run-git (append (list "log" + "--pretty=oneline" + "--skip" (princ-to-string offset)) + n-arg))))) + (loop for line in lines + for space-position = (position #\space line) + for small-hash = (subseq line 0 hash-length) + for message = (subseq line space-position) + collect (list :line (concatenate 'string small-hash message) + :message message + :hash small-hash)))) + +(defmethod commits-log ((vcs vcs-git) &key (offset 0) limit (hash-length 8)) + (latest-commits vcs :n limit + :hash-length hash-length + :offset offset)) + +(defmethod commit-count ((vcs vcs-git)) + (parse-integer + (str:trim (run-git '("rev-list" "--count" "HEAD"))))) + +(defmethod stage ((vcs vcs-git) file) + (run-git (list "add" file))) + +(defmethod unstage ((vcs vcs-git) file) + "Unstage changes to a file." + (run-git (list "reset" "HEAD" "--" file))) + +(defmethod discard-file ((vcs vcs-git) file) + "Discard all the changes to this file. + + This currently means: checkout this file." + (run-git (list "checkout" file))) + +(defun %maybe-lem-home () + "Return Lem's home directory by calling lem:lem-home only if the :lem package exists, + otherwise return ~/.lem/. + We don't want a hard-coded dependency on Lem in the porcelain package, to ease testing." + (if (find-package :lem) + (uiop:symbol-call :lem :lem-home) + (merge-pathnames ".lem/" (user-homedir-pathname)))) + +(defmethod apply-patch ((vcs vcs-git) diff &key reverse) + "Apply a patch file. + This is used to stage hunks of files." + (when *verbose* + (log:info diff) + (with-open-file (f (merge-pathnames "lem-hunk-latest.patch" (%maybe-lem-home)) + :direction :output + :if-exists :supersede + :if-does-not-exist :create) + (write-sequence diff f))) + + ;; Write diff on file. + ;; It should also be possible to give it on stdin ("git apply - ") + ;; (uiop:with-temporary-file (:stream f) ;; issues with this. + (with-open-file (f ".lem-hunk.patch" + :direction :output + :if-exists :supersede + :if-does-not-exist :create) + (write-sequence diff f) + (finish-output f)) + + (let ((base (list "apply" + "--ignore-space-change" ;; in context only. + "-C0" ;; easier to apply patch without context. + "--index" + "--cached")) + (maybe (if reverse + (list "--reverse") + (list))) + (arglist (list ".lem-hunk.patch"))) + (run-git (concatenate 'list base maybe arglist)))) + +(defun root-commit-p (hash) + "Find this repository's very first commit on the current branch, + return T if this commit hash is the root. + + hash: (string) can be a short commit hash or an entire one. + + This check is required when doing a git interactive rebase." + ;; the git command + ;; git rebase --interactive a1b2c3^ + ;; fails if a1b2c3 is the root commit. + ;; We must use --root instead. + (let ((root (run-git (list "rev-list" + "--max-parents=0" + "HEAD")))) + ;; We use small hashes, so don't use equal. + (str:starts-with-p hash root))) + + +;; +;; Git interactive rebase. +;; +;; -i --autostash +;; If rebase from first commit: use --root +;; otherwise use ^ +;; exple: git rebase --autostash -i 317315966^ +;; This creates a file in .git/rebase-merge/git-rebase-merge-todo +;; which we edit with Lem, and we validate the rebase process. +;; + +(defvar *rebase-script-path*) + +;; Save our script as a string at compile time. +(defparameter *rebase-script-content* + #+(or lem-ncurses lem-sdl2) + (str:from-file + (asdf:system-relative-pathname (asdf:find-system "lem-legit") + "scripts/dumbrebaseeditor.sh")) + #-(or lem-ncurses lem-sdl2) + "" + "Our dumb editor shell script, saved as a string at compile time. + We then save it to the user's ~/.lem/legit/rebaseetidor.sh at first use.") + +(defun rebase-script-path () + (if (boundp '*rebase-script-path*) + *rebase-script-path* + (let* ((legit-path (merge-pathnames "legit/" (%maybe-lem-home))) + (script-path (namestring (uiop:merge-pathnames* "dumbrebaseeditor.sh" legit-path)))) + (ensure-directories-exist legit-path) + (unless (uiop:file-exists-p script-path) + (str:to-file script-path *rebase-script-content*)) + ;; Ensure the file is executable. + #+unix + (uiop:run-program (list "chmod" "+x" script-path) + :output :string + :error-output :string + :ignore-error-status t) + #-unix + (porcelain-error "lem/legit: our rebase script is only for Unix platforms currently. We need to run a shell script and trap a signal.") + (setf *rebase-script-path* script-path)))) + +(defmethod rebase-interactively ((vcs vcs-git) &key from) + "Start a rebase session. + + Then edit the git rebase file and validate the rebase with `rebase-continue` + or stop it with `rebase-abort`. + + from: commit hash (string) to start the rebase from. + + Return three values suitable for `legit:run-function`: output string, error output string, exit code (integer)." + ;; For testing, go to a test project (,cd on Slime), and edit this project's + ;; .git/rebase-merge/git-rebase-merge-todo + ;; Beware of C-c ~ lol^^ + (when (uiop:directory-exists-p ".git/rebase-merge/") + (porcelain-error "It seems that there is already a rebase-merge directory, +and I wonder if you are in the middle of another rebase. +If that is the case, please try + git rebase (--continue | --abort | --skip) +If that is not the case, please + rm -fr \".git/rebase-merge\" +and run me again. +I am stopping in case you still have something valuable there.")) + + (unless from + (return-from rebase-interactively + (values "Git rebase is missing the commit to rebase from. We are too shy to rebase everything from the root commit yet. Aborting" + nil + 1))) + + (let ((editor (uiop:getenv "EDITOR"))) + (setf (uiop:getenv "EDITOR") (rebase-script-path)) + (unwind-protect + ;; xxx: get the error output, if any, to get explanations of failure. + (let ((process (uiop:launch-program (list + "git" + "rebase" + "--autostash" + "-i" + ;; Give the right commit to rebase from. + ;; When rebasing from the root commit, + ;; something special? + (if (root-commit-p from) + "--root" + (format nil "~a^" from))) + :output :stream + :error-output :stream + :ignore-error-status t))) + (if (uiop:process-alive-p process) + (let* ((output (read-line (uiop:process-info-output process))) + (pidtxt (str:trim (second (str:split ":" output))))) + (setf (rebase-pid vcs) pidtxt) + (format t "The git interactive rebase is started on pid ~a. Edit the rebase file and validate." pidtxt) + (values (format nil "rebase started") + nil + 0)) + (porcelain-error "git rebase process didn't start properly. Aborting."))) + (setf (uiop:getenv "EDITOR") editor)))) + +(defun %rebase-signal (vcs &key (sig "-SIGTERM")) + (declare (type vcs-git vcs)) + "Send a kill signal to our rebase script: with -SIGTERM, git picks up our changes and continues the rebase process. This is called by a rebase continue command. + With -SIGKILL the process is stopped. This is called by rebase abort." + (multiple-value-bind (output error-output exit-code) + (uiop:run-program (list "kill" sig (rebase-pid vcs)) + :output :string + :error-output :string + :ignore-error-status t) + (declare (ignorable output)) + (setf (rebase-pid vcs) nil) + (values (format nil "rebase finished.") + error-output + exit-code))) + +(defmethod rebase-continue ((vcs vcs-git)) + (cond + ;; First, if we are running our rebase script, send a "continue" signal + ;; so that git continues the rebase. + ;; This is used by C-c C-c in the interactive rebase buffer. + ((rebase-pid vcs) + (%rebase-signal vcs)) + ;; Check if a rebase was started by someone else and continue it. + ;; This is called from legit interace: "r" "c". + ((uiop:directory-exists-p ".git/rebase-merge/") + (run-git (list "rebase" "--continue"))) + (t + (porcelain-error "No git rebase in process?")))) +(defmethod rebase-abort ((vcs vcs-git)) + (cond + ;; First, if we are running our rebase script, kill it. + ;; This makes git abort the rebase too. + ;; This is used by C-c C-k in the interactive rebase buffer. + ((rebase-pid vcs) + (%rebase-signal vcs :sig "-SIGKILL")) + ;; Check if a rebase was started by someone else and abort it. + ;; This is called from legit interface: "r" "a". + ((uiop:directory-exists-p ".git/rebase-merge/") + (run-git (list "rebase" "--abort"))) + (t + (porcelain-error "No git rebase in process? PID not found.")))) + +(defmethod rebase-skip ((vcs vcs-git)) + (cond + ((rebase-pid vcs) + (porcelain-error "The rebase process you started from Lem is still running. Please continue or abort it (or kill job ~a)" (rebase-pid vcs))) + ;; Check if a rebase was started by someone else and abort it. + ;; This is called from legit interface: "r" "s". + ((uiop:directory-exists-p ".git/rebase-merge/") + (run-git (list "rebase" "--skip"))) + (t + (porcelain-error "No git rebase in process? PID not found.")))) diff --git a/extensions/legit/porcelain-hg.lisp b/extensions/legit/porcelain-hg.lisp new file mode 100644 index 000000000..d7f91afac --- /dev/null +++ b/extensions/legit/porcelain-hg.lisp @@ -0,0 +1,206 @@ +(uiop:define-package :lem/porcelain/hg + (:use :cl + :lem/porcelain) + (:shadow :push) + (:import-from :trivial-types + :proper-list) + (:export :hg-project-p) + (:documentation "Implements the porcelain interface for hg-based repos.")) + +(in-package :lem/porcelain/hg) + +(declaim (type (proper-list string) *hg-base-arglist*)) +(defvar *hg-base-arglist* (list "hg") + "The mercurial program (hg), to be appended command-line options.") + +(defvar *projects-mapping* (make-hash-table :test #'equal) + "Map a VCS project root to its VCS object. + + We need a single VCS object to retain data. + + Note: no special data is saved for Mercurial projects yet.") + +(defun get-or-create-project () + "If a VCS object exists for the current project root (as of + the CWD, set by `with-current-project', return it. Otherwise, create a + new `vcs-hg' object instance." + (let* ((root (uiop:getcwd)) ;; CWD set by with-current-project. + (existing (gethash root *projects-mapping*))) + (or existing + (setf (gethash root *projects-mapping*) + (make-instance 'vcs-hg))))) + +;; VCS implementation for hg +(defclass vcs-hg (vcs-project) + () + (:default-initargs + :name "mercurial")) + +(defun hg-project-p () + "Return t if we find a .hg/ directory in the current directory (which should be the project root. Use `lem/porcelain:with-current-project`)." + (when (uiop:directory-exists-p ".hg") + (get-or-create-project))) + +(defun run-hg (arglist) + "Run the mercurial command with these options (list). + Return standard and error output as strings. + + For error handling strategy, see `run-git`." + (uiop:run-program (concatenate 'list + *hg-base-arglist* + (uiop:ensure-list arglist) + '("--pager" "never") ;; no interactive pager. + ) + :output :string + :error-output :string + :ignore-error-status t)) + +(defun porcelain () + "Return changes." + (run-hg "status")) + +(defmethod components ((vcs vcs-hg)) + "Return 3 values: + - untracked files + - modified and unstaged files + - modified and staged files." + ;; Mercurial manual: + ;; The codes used to show the status of files are: + ;; M = modified + ;; A = added + ;; R = removed => difference with Git + ;; C = clean => difference + ;; ! = missing (deleted by non-hg command, but still tracked) + ;; ? = not tracked + ;; I = ignored (=> not shown without -A) + ;; = origin of the previous file (with --copies) + (loop for line in (str:lines (porcelain)) + for file = (subseq line 2) ;; a slight difference than with git. + unless (str:blankp line) + ;; Modified + if (equal (elt line 0) #\M) + collect (list :file file :type :modified) into modified-staged-files + ;; Added + if (equal (elt line 0) #\A) + collect (list :file file :type :added) into modified-staged-files + ;; Removed + if (equal (elt line 0) #\R) + collect (list :file file :type :deleted) into modified-staged-files + ;; Modified (unstaged) + if (or (equal (elt line 1) #\M) (equal (elt line 1) #\X)) + collect (list :file file :type :modified) into modified-unstaged-files + ;; Untracked + if (str:starts-with-p "?" line) + collect file into untracked-files + ;; Missing (deleted) + if (str:starts-with-p "!" line) + collect (list :file file :type :deleted) into modified-unstaged-files + finally (return (values untracked-files + modified-unstaged-files + modified-staged-files)))) + +(defmethod file-diff ((vcs vcs-hg) file &key cached) + "Show the diff of staged files (and only them)." + (when cached + (run-hg (list "diff" + (format nil "-U~D" lem/porcelain:*diff-context-lines*) + file)) + ;; files not staged can't be diffed. + ;; We could read and display their full content anyways? + )) + +(defmethod show-commit-diff ((vcs vcs-hg) ref &key ignore-all-space) + (declare (ignore ignore-all-space)) + (run-hg (list "log" "-r" ref "-p"))) + + +(defmethod commit ((vcs vcs-hg) message) + (run-hg (list "commit" "-m" message))) + +(defmethod current-branch ((vcs vcs-hg)) + "Return the current branch name." + (str:trim + (run-hg "branch"))) + +(defmethod branches ((vcs vcs-hg) &key sort-by) + (declare (ignore sort-by)) + (porcelain-error "not implemented")) + +(defmethod latest-commits ((vcs vcs-hg) &key (n lem/porcelain:*nb-latest-commits*) (hash-length 8) (offset 0)) + (declare (ignorable n hash-length offset)) + (let ((out (run-hg "log"))) + ;; Split by paragraph. + #| $ hg log + changeset: 1:c20c766359d3 + user: user@machine + date: Mon Oct 02 23:01:32 2023 +0200 + summary: second line + + changeset: 0:b27dda897ba8 + user: user@machine + date: Mon Oct 02 22:51:57 2023 +0200 + summary: test + + |# + (loop for paragraph in (ppcre:split "\\n\\n" out) + collect + (loop for line in (str:lines (str:trim paragraph)) + with entry = (list :line "" + :message "" + :hash "") + for (key %val) = (str:split ":" line :limit 2) + for val = (str:trim %val) + for changeset = "" + for tag = "" + for user = "" + for date = "" + for summary = "" + do + (cond + ;; we can use str:string-case with a recent enough quicklisp dist > July 2023 (PR 103) + ((equal key "changeset") + (setf changeset val) + (setf (getf entry :changeset) val) + (setf (getf entry :hash) val)) + ((equal key "tag") + (setf tag val) + (setf (getf entry :tag) val)) + ((equal key "user") + (setf user val) + (setf (getf entry :user) val)) + ((equal key "date") + (setf date val) + (setf (getf entry :date) val)) + ((equal key "summary") + (setf summary (str:trim val)) + (setf (getf entry :summary) val) + (setf (getf entry :message) + (str:concat " " val)) + (setf (getf entry :line) + (str:concat changeset " " summary)))) + finally (return entry))))) + +(defmethod commits-log ((vcs vcs-hg) &key (offset 0) limit (hash-length 8)) + (declare (ignorable hash-length)) + (let* ((commits (latest-commits vcs)) + (total-commits (length commits)) + (end (when limit + (min total-commits (+ offset limit))))) + (if (>= offset total-commits) + nil + (subseq commits offset end)))) + +(defmethod commit-count ((vcs vcs-hg)) + (parse-integer + (str:trim (run-hg '("id" "--num" "--rev" "tip"))))) + +(defmethod stage ((vcs vcs-hg) file) + (run-hg (list "add" file))) + +(defmethod apply-patch ((vcs vcs-hg) diff &key reverse) + (declare (ignorable diff reverse)) + (porcelain-error "applying patch not yet implemented for Mercurial")) + +(defmethod rebase-interactively ((vcs vcs-hg) &key from) + (declare (ignore from)) + (porcelain-error "Interactive rebase not implemented for Mercurial")) diff --git a/extensions/legit/porcelain.lisp b/extensions/legit/porcelain.lisp index 448529f53..a331f34ce 100644 --- a/extensions/legit/porcelain.lisp +++ b/extensions/legit/porcelain.lisp @@ -1,12 +1,13 @@ -(defpackage :lem/porcelain +(uiop:define-package :lem/porcelain (:use :cl) + ;; beware, we shadow cl:push to have a "push" method: (:shadow :push) (:import-from :trivial-types :proper-list) (:export - :*vcs* :porcelain-error + :message :apply-patch :branches :checkout @@ -21,16 +22,21 @@ :push :rebase-abort :rebase-continue + :rebase-interactively :rebase-skip + :rebase-in-progress-p :show-commit-diff :stage :unstage - :vcs-project-p - ) + :*diff-context-lines* + :commits-log + :*commits-log-page-size* + :commit-count + :*nb-latest-commits* + :vcs-project) (:documentation "Functions to run VCS operations: get the list of changes, of untracked files, commit, push… Git support is the main goal, a simple layer is used with other VCS systems (Fossil, Mercurial). On interactive commands, Legit will check what VCS is in use in the current project. -When used programmatically, bind the `lem/porcelain:*vcs*` variable in your caller. See `lem/legit:with-current-project`. When multiple VCS are used at the same time in a project, Git takes precedence by default. See `lem/porcelain:*vcs-existence-order*`.")) @@ -45,46 +51,21 @@ Supported version control systems: TODOs: -- show missing files. - Mercurial: - add (stage) diff hunks |# -(declaim (type (proper-list string) *git-base-arglist*)) -(defvar *git-base-arglist* (list "git") - "The git program, to be appended command-line options.") - -(declaim (type (proper-list string) *fossil-base-args*)) -(defvar *fossil-base-args* (list "fossil") - "The fossil program, to be appended command-line options.") - -(declaim (type (proper-list string) *hg-base-arglist*)) -(defvar *hg-base-arglist* (list "hg") - "The mercurial program (hg), to be appended command-line options.") - ;;; Global variables, for all VCS. (defvar *nb-latest-commits* 10 "Number of commits to show in the status.") -(defvar *branch-sort-by* "-creatordate" - "When listing branches, sort by this field name. - Prefix with \"-\" to sort in descending order. - - Defaults to \"-creatordate\", to list the latest used branches first.") - -(defvar *file-diff-args* (list "diff" "--no-color") - "Arguments to display the file diffs. - Will be surrounded by the git binary and the file path. +(defvar *commits-log-page-size* 200 + "Number of commits to show in the commits log.") - For staged files, --cached is added by the command.") - -(defvar *vcs* nil - "git, fossil? For current project. Bind this in the caller. - - For instance, see the legit::with-current-project macro that lexically binds *vcs* for an operation.") +(defvar *diff-context-lines* 4 + "How many lines of context before/after the first committed line") (define-condition porcelain-condition (simple-error) ()) @@ -102,475 +83,114 @@ Mercurial: (defun porcelain-error (message &rest args) (error 'porcelain-error :message (apply #'format nil message args))) - -(defun git-project-p () - "Return t if we find a .git/ directory in the current directory (which should be the project root. Use `lem/legit::with-current-project`)." - (values (uiop:directory-exists-p ".git") - :git)) - -(defun fossil-project-p () - "Return t if we either find a .fslckout file in the current directory (should be the project root) or a file with a .fossil extension." - (cond - ((uiop:file-exists-p ".fslckout") - (values ".fslckout" :fossil)) - (t - ;; Maybe do we need "fossil open" here. - (loop for file in (uiop:directory-files "./") - if (equal "fossil" (pathname-type file)) - return (values file :fossil))))) - -(defun hg-project-p () - "Return t if we find a .hg/ directory in the current directory (which should be the project root. Use `lem/legit::with-current-project`)." - (values (uiop:directory-exists-p ".hg") - :hg)) - -(defvar *vcs-existence-order* - '( - git-project-p - fossil-project-p - hg-project-p - )) - -(defun vcs-project-p () - "Is this project under a known version control system? - Return two values: the current directory (pathname), which is then considered the project root, and the VCS found (:git, :fossil or :hg)." - ;; This doesn't return the 2 values :( - ;; (or (fossil-project-p) - ;; (git-project-p)) - (loop for fn in *vcs-existence-order* - do (multiple-value-bind (root vcs) - (funcall fn) - (if root - (return (values root vcs)))))) - -(defun run-git (arglist) - "Run the git command with these options (list). - Return standard and error output as strings. - - Don't signal an error if the process doesn't exit successfully - (but return the error message, so it can be displayed by the caller). - However, if uiop:run-program fails to run the command, the interactive debugger - is invoked (and shown correctly in Lem)." - (uiop:run-program (concatenate 'list - *git-base-arglist* - (uiop:ensure-list arglist)) - :output :string - :error-output :string - :ignore-error-status t)) - -(defun run-fossil (arglist) - "Run the fossil command with these options. - - arglist: a list of CLI options and arguments to append to the base fossil program. - See `*fossil-base-args*`." - (uiop:run-program (concatenate 'list - *fossil-base-args* - (uiop:ensure-list arglist)) - :output :string - :error-output :string - :ignore-error-status t)) - -(defun run-hg (arglist) - "Run the mercurial command with these options (list). - Return standard and error output as strings. - - For error handling strategy, see `run-git`." - (uiop:run-program (concatenate 'list - *hg-base-arglist* - (uiop:ensure-list arglist) - '("--pager" "never") ;; no interactive pager. - ) - :output :string - :error-output :string - :ignore-error-status t)) - - +(defclass vcs-project () + ((name :initform "" + :initarg :name + :accessor vcs-name + :documentation "VCS name: git, fossil, mercurial, etc.")) + (:documentation "Base class to represent version-controlled projects. + + A vcs-project object must be unique per project (as its root directory, as the CWD). See `vcs-project-p'. + + Each VCS subclass can add slots to store project-specific variables. For example, for git, we must store the ongoing rebase PID.")) + +(defmethod print-object ((vcs vcs-project) stream) + (print-unreadable-object (vcs stream :type t :identity t) + (format stream "VCS: ~s" (vcs-name vcs)))) + + ;;; ;;; Getting changes. ;;; +(defgeneric components (vcs) + (:documentation "Returns three values, each a List[string] +- Untracked files +- Modified and unstaged files +- Modified and staged files") + (:method (vcs) + (porcelain-error "lem/porcelain:components not implemented for vcs ~a" (vcs-name vcs)))) -(defun git-porcelain () - "Return the git status: for each file in the project, the `git status --porcelain` command -allows to learn about the file state: modified, deleted, ignored… " - (run-git (list "status" "--porcelain=v1"))) - -(defun hg-porcelain () - "Return changes." - (run-hg "status")) - -(defun fossil-porcelain () - "Get changes." - (multiple-value-bind (out error code) - (run-fossil "changes") - (cond - ((not (zerop code)) - (porcelain-error (str:join #\newline (list out error)))) - (t - (values out error))))) - -;; Dispatching on the right VCS: -;; *vcs* is bound in the caller (in legit::with-current-project). -(defun porcelain () - "Dispatch on the current `*vcs*` and get current changes (added, modified files…)." - (case *vcs* - (:git (git-porcelain)) - (:fossil (fossil-porcelain)) - (:hg (hg-porcelain)) - (t (porcelain-error "VCS not supported: ~a" *vcs*)))) - -(defun git-components() - "Return 3 values: - - untracked files - - modified and unstaged files - - modified and stages files. - - Git manual: - - In the short-format, the status of each path is shown as one of these forms - XY PATH - XY ORIG_PATH -> PATH - For paths with merge conflicts, X and Y show the modification states of each side of the - merge. For paths that do not have merge conflicts, X shows the status of the index, and Y - shows the status of the work tree. For untracked paths, XY are ??. Other status codes can - be interpreted as follows: - • ' ' = unmodified - • M = modified - • A = added - • D = deleted - • R = renamed - • C = copied - • U = updated but unmerged" - (loop for line in (str:lines (porcelain)) - for file = (subseq line 3) - unless (str:blankp line) - if (equal (elt line 0) #\M) - collect file into modified-staged-files - if (equal (elt line 0) #\A) - ;; Here we don't differentiate between modified and newly added. - ;; We could do better. - collect file into modified-staged-files - if (equal (elt line 1) #\M) - collect file into modified-unstaged-files - if (str:starts-with-p "??" line) - collect file into untracked-files - finally (return (values untracked-files - modified-unstaged-files - modified-staged-files)))) - -(defun hg-components () - "Return 3 values: - - untracked files - - modified and unstaged files - - modified and staged files." - ;; Mercurial manual: - ;; The codes used to show the status of files are: - ;; M = modified - ;; A = added - ;; R = removed => difference with Git - ;; C = clean => difference - ;; ! = missing (deleted by non-hg command, but still tracked) - ;; ? = not tracked - ;; I = ignored (=> not shown without -A) - ;; = origin of the previous file (with --copies) - (loop for line in (str:lines (hg-porcelain)) - for file = (subseq line 2) ;; a slight difference than with git. - unless (str:blankp line) - ;; Modified - if (equal (elt line 0) #\M) - collect file into modified-staged-files - ;; Added - if (equal (elt line 0) #\A) - ;; is that enough? - collect file into modified-staged-files - ;; Modified - if (equal (elt line 1) #\X) ;? - collect file into modified-unstaged-files - ;; Untracked - if (str:starts-with-p "?" line) - collect file into untracked-files - ;; Missing (deleted) - if (str:starts-with-p "!" line) - collect file into missing-files - finally (return (values untracked-files - modified-unstaged-files - modified-staged-files - missing-files)))) -(defun fossil-components () - "Return values: - - untracked files (todo) - - list of ADDED files - - modified files" - (loop for line in (str:lines (fossil-porcelain)) - for parts = (str:words line) - for status = (first parts) - for file = (second parts) - if (equal "ADDED" status) - collect file into added-files - if (equal "EDITED" status) - collect file into edited-files - finally (return (values nil - added-files - edited-files)))) - -(defun components () - (case *vcs* - (:git (git-components)) - (:fossil (fossil-components)) - (:hg (hg-components)) - (t (porcelain-error "VCS not supported: ~a" *vcs*)))) - - ;;; ;;; diff ;;; -(defun git-file-diff (file &key cached) - ;; --cached is a synonym for --staged. - ;; So it is set only for staged files. From git-components: the 3rd value, modified and staged files. - (run-git - (concatenate 'list - *file-diff-args* - (if cached '("--cached")) - (list file)))) - -(defun hg-file-diff (file &key cached) - "Show the diff of staged files (and only them)." - (when cached - (run-hg (list "diff" file)) - ;; files not staged can't be diffed. - ;; We could read and display their full content anyways? - )) - -(defun fossil-file-diff (file &key cached) - (declare (ignorable cached)) - (run-fossil (list "diff" file))) - -(defun file-diff (file &key cached) - (case *vcs* - (:fossil - (fossil-file-diff file)) - (:hg - (hg-file-diff file :cached cached)) - (t - (git-file-diff file :cached cached)))) - - +(defgeneric file-diff (vcs file &key cached) + (:method (vcs file &key cached) + (declare (ignorable file cached)) + (porcelain-error "lem/porcelain:file-diff not implemented for vcs ~a" (vcs-name vcs)))) + ;;; ;;; Show commits. ;;; -(defun git-show-commit-diff (ref &key ignore-all-space) - (let ((options '())) - (when ignore-all-space - (cl:push "-w" options)) - (run-git `("show" ,@options ,ref)))) - -(defun hg-show-commit-diff (ref) - (run-hg (list "log" "-r" ref "-p"))) - -(defun show-commit-diff (ref &key ignore-all-space) - (case *vcs* - (:fossil nil) - (:hg (hg-show-commit-diff ref)) - (t (git-show-commit-diff ref :ignore-all-space ignore-all-space)))) +(defgeneric show-commit-diff (vcs ref &key ignore-all-space) + (:method (vcs ref &key ignore-all-space) + (declare (ignorable ref ignore-all-space)) + (porcelain-error "lem/porcelain:show-commit-diff not implemented for vcs ~a" (vcs-name vcs)))) ;; commit -(defun git-commit (message) - (run-git (list "commit" "-m" message))) +(defgeneric commit (vcs message) + (:documentation "Performs a commit operation: `message` must be a string.") + (:method (vcs message) + (declare (ignorable message)) + (porcelain-error "lem/porcelain:commit not implemented for vcs ~a" (vcs-name vcs)))) -(defun hg-commit (message) - (run-hg (list "commit" "-m" message))) +;; branches +(defgeneric branches (vcs &key sort-by) + (:documentation "Returns a list[str] of branches in the repository") + (:method (vcs &key sort-by) + (declare (ignorable vcs sort-by)) + (porcelain-error "Method branches not supported for vcs ~a" (vcs-name vcs)))) -(defun fossil-commit (message) - (run-fossil (list "commit" "-m" message))) +(defgeneric current-branch (vcs) + (:documentation "Return the current branch name (string).") + (:method (vcs) + (porcelain-error "lem/porcelain:current-branch not implemented for vcs ~a" (vcs-name vcs)))) -(defun commit (message) - (case *vcs* - (:fossil (fossil-commit message)) - (:hg (hg-commit message)) - (t (git-commit message)))) +(defgeneric rebase-in-progress-p (vcs) + (:documentation + "Return a plist if a rebase is in progress. Used for legit-status. -;; branches -(defun git-list-branches (&key (sort-by *branch-sort-by*)) - "Return: list of branches, raw output. - Each element starts with two meaningful characters, such as \"* \" for the current branch." - (str:lines - (run-git (list "branch" - "--list" - "--no-color" - (format nil "--sort=~a" sort-by))))) - -(defun branches (&key (sort-by *branch-sort-by*)) - (loop for branch in (git-list-branches :sort-by sort-by) - collect (subseq branch 2 (length branch)))) - -(defun fossil-branches (&key &allow-other-keys) - (porcelain-error "not implemented")) - -(defun git-current-branch () - (let ((branches (git-list-branches :sort-by "-creatordate"))) - (loop for branch in branches - if (str:starts-with-p "*" branch) - return (subseq branch 2)))) - -(defun hg-current-branch () - "Return the current branch name." - (str:trim - (run-hg "branch"))) - -(defun fossil-current-branch () - ;; strip out "* " from "* trunk" - (str:trim - (subseq (run-fossil "branch") 2))) - -(defun current-branch () - "Return the current branch name." - (case *vcs* - (:fossil - (fossil-current-branch)) - (:hg - (hg-current-branch)) - (t - (git-current-branch)))) - -(defun rebase-in-progress () - "Return a plist if a rebase is in progress. Used for legit-status. - - plist keys: + Ignore this check for all VCSs that don't support rebases and return no values. + + Return: a plist, with keys: :status (boolean) -> T if a rebase is in progress :head-name -> content from .git/rebase-merge/head-name, such as \"refs/heads/master\" :head-short-name -> \"master\" - :onto -> content from .git/rebase-merge/onto, a commit id." - (case *vcs* - (:git - (when (uiop:directory-exists-p ".git/rebase-merge/") - (let ((head (str:trim (str:from-file ".git/rebase-merge/head-name"))) - (onto (str:trim (str:from-file ".git/rebase-merge/onto")))) - (list :status t - :head-name head - :head-short-name (or (third (str:split "/" head)) - head) - :onto onto - :onto-short-commit (str:shorten 8 onto :ellipsis ""))))) - (t - (log:info "rebase not available for ~a" *vcs*) - (values)))) + :onto -> content from .git/rebase-merge/onto, a commit id.") + (:method (vcs) + (values))) ;;; ;;; Latest commits. ;;; -(defun %git-list-latest-commits (&optional (n *nb-latest-commits*)) - (when (plusp n) - (str:lines - (run-git (list "log" "--pretty=oneline" "-n" (princ-to-string n)))))) - -(defun git-latest-commits (&key (hash-length 8)) - (let ((lines (%git-list-latest-commits))) - (loop for line in lines - for space-position = (position #\space line) - for small-hash = (subseq line 0 hash-length) - for message = (subseq line space-position) - collect (list :line (concatenate 'string small-hash message) - :message message - :hash small-hash)))) - -(defun hg-latest-commits (&key (hash-length 8)) - (declare (ignorable hash-length)) - (let ((out (run-hg "log"))) - ;; Split by paragraph. - #| $ hg log -changeset: 1:c20c766359d3 -user: user@machine -date: Mon Oct 02 23:01:32 2023 +0200 -summary: second line - -changeset: 0:b27dda897ba8 -user: user@machine -date: Mon Oct 02 22:51:57 2023 +0200 -summary: test +(defgeneric latest-commits (vcs &key n hash-length offset) + (:documentation "Return a list of strings or plists. + The plist has a :hash and a :message, or simply a :line.")) -|# - (loop for paragraph in (ppcre:split "\\n\\n" out) - collect - (loop for line in (str:lines (str:trim paragraph)) - with entry = (list :line "" - :message "" - :hash "") - for (key %val) = (str:split ":" line :limit 2) - for val = (str:trim %val) - for changeset = "" - for tag = "" - for user = "" - for date = "" - for summary = "" - do - (cond - ;; we can use str:string-case with a recent enough quicklisp dist > July 2023 (PR 103) - ((equal key "changeset") - (setf changeset val) - (setf (getf entry :changeset) val) - (setf (getf entry :hash) val)) - ((equal key "tag") - (setf tag val) - (setf (getf entry :tag) val)) - ((equal key "user") - (setf user val) - (setf (getf entry :user) val)) - ((equal key "date") - (setf date val) - (setf (getf entry :date) val)) - ((equal key "summary") - (setf summary (str:trim val)) - (setf (getf entry :summary) val) - (setf (getf entry :message) - (str:concat " " val)) - (setf (getf entry :line) - (str:concat changeset " " summary)))) - finally (return entry))))) - - -(defun fossil-latest-commits (&key &allow-other-keys) - ;; return bare result. - (str:lines (run-fossil "timeline"))) - -(defun latest-commits (&key (hash-length 8)) - "Return a list of strings or plists. - The plist has a :hash and a :message, or simply a :line." - (case *vcs* - (:fossil - (fossil-latest-commits)) - (:hg - (hg-latest-commits)) - (t - (git-latest-commits :hash-length hash-length)))) +(defgeneric commits-log (vcs &key offset limit hash-length) + (:documentation + "Return a list of commits starting from the given offset. + If a limit is not provided, it returns all commits after the offset.")) + +(defgeneric commit-count (vcs) + (:documentation + "Returns: integer representing number of commits in the current branch.") + (:method (vcs) + (porcelain-error "lem/porcelain:commit-count not implemented for vcs ~a" (vcs-name vcs)))) ;; stage, add files. -(defun git-stage (file) - (run-git (list "add" file))) - -(defun hg-stage (file) - (run-hg (list "add" file))) - -(defun fossil-stage (file) - (run-fossil (list "add" file))) - -(defun stage (file) - (case *vcs* - (:fossil (fossil-stage file)) - (:hg (hg-stage file)) - (t (git-stage file)))) - -(defun unstage (file) - "Unstage changes to this file. - The reverse of \"add\"." - (case *vcs* - (:git (git-unstage file)) - (:hg (hg-unstage file)) - (:fossil (porcelain-error "unstage not implemented for Fossil.")) - (t (porcelain-error "VCS not supported: ~a" *vcs*)))) - -(defun git-unstage (file) - "Unstage changes to a file." - (run-git (list "reset" "HEAD" "--" file))) +(defgeneric stage (vcs file) + (:documentation "Stage changes to a file.") + (:method (vcs file) + (declare (ignorable file)) + (porcelain-error "lem/porcelain:stage not implemented for vcs ~a" (vcs-name vcs)))) + +(defgeneric unstage (vcs file) + (:documentation + "Unstage changes to this file. The reverse of \"add\".") + (:method (vcs file) + (declare (ignorable file)) + (porcelain-error "VCS does not support or legit does not implement unstage: ~a" (vcs-name vcs)))) #| Interestingly, this returns the list of unstaged changes: "Unstaged changes after reset: @@ -580,291 +200,60 @@ M src/ext/porcelain.lisp "" |# -(defun hg-unstage (file) - (declare (ignorable file)) - ;; no index like git, we'd need to exclude files from the commit with -X ? - (porcelain-error "no unstage support for Mercurial")) - ;; discard changes. -(defun git-discard-file (file) - "Discard all the changes to this file. - - This currently means: checkout this file." - (run-git (list "checkout" file))) - -(defun discard-file (file) - (case *vcs* - (:git (git-discard-file file)) - (t - (porcelain-error "discard-file is not implemented for this VCS: ~a" *vcs*)))) - -(defvar *verbose* nil) - -(defun git-apply-patch (diff &key reverse) - "Apply a patch file. - This is used to stage hunks of files." - (when *verbose* - (log:info diff) - (with-open-file (f (merge-pathnames "lem-hunk-latest.patch" (%maybe-lem-home)) - :direction :output - :if-exists :supersede - :if-does-not-exist :create) - (write-sequence diff f))) - - ;; Write diff on file. - ;; It should also be possible to give it on stdin ("git apply - ") - ;; (uiop:with-temporary-file (:stream f) ;; issues with this. - (with-open-file (f ".lem-hunk.patch" - :direction :output - :if-exists :supersede - :if-does-not-exist :create) - (write-sequence diff f) - (finish-output f)) - - (let ((base (list "apply" - "--ignore-space-change" ;; in context only. - "-C0" ;; easier to apply patch without context. - "--index" - "--cached")) - (maybe (if reverse - (list "--reverse") - (list))) - (arglist (list ".lem-hunk.patch"))) - (run-git (concatenate 'list base maybe arglist)))) - -(defun fossil-apply-patch (diff &key reverse) - "Needs fossil at least > 2.10. Version 2.22 works." - (declare (ignorable diff reverse)) - ;; mmh… it wants a binary patch. - (values nil "fossil patch is not supported." 1)) - -(defun apply-patch (diff &key reverse) - "Apply a patch from this diff. - diff: string." - (case *vcs* - (:fossil (fossil-apply-patch diff :reverse reverse)) - (:hg (porcelain-error "applying patch not yet implemented for Mercurial")) - (t (git-apply-patch diff :reverse reverse)))) - -(defun checkout (branch) - (run-git (list "checkout" branch))) - -(defun checkout-create (new start) - (run-git (list "checkout" "-b" new start))) - -(defun pull () - ;; xxx: recurse submodules, etc. - (run-git (list "pull" "HEAD"))) - -(defun push (&rest args) - (when args - (porcelain-error "Our git push command doesn't accept args. Did you mean cl:push ?!!")) - (run-git (list "push"))) - -;; -;; Git interactive rebase. -;; -;; -i --autostash -;; If rebase from first commit: use --root -;; otherwise use ^ -;; exple: git rebase --autostash -i 317315966^ -;; This creates a file in .git/rebase-merge/git-rebase-merge-todo -;; which we edit with Lem, and we validate the rebase process. -;; - -(defvar *rebase-script-path*) - -;; Save our script as a string at compile time. -(defparameter *rebase-script-content* - #+(or lem-ncurses lem-sdl2) - (str:from-file - (asdf:system-relative-pathname (asdf:find-system "lem") - "scripts/dumbrebaseeditor.sh")) - #-(or lem-ncurses lem-sdl2) - "" - "Our dumb editor shell script, saved as a string at compile time. - We then save it to the user's ~/.lem/legit/rebaseetidor.sh at first use.") - -(defun %maybe-lem-home () - "Return Lem's home directory by calling lem:lem-home only if the :lem package exists, - otherwise return ~/.lem/. - We don't want a hard-coded dependency on Lem in the porcelain package, to ease testing." - (if (find-package :lem) - (uiop:symbol-call :lem :lem-home) - (merge-pathnames ".lem/" (user-homedir-pathname)))) - -(defun rebase-script-path () - (if (boundp '*rebase-script-path*) - *rebase-script-path* - (let* ((legit-path (merge-pathnames "legit/" (%maybe-lem-home))) - (script-path (namestring (uiop:merge-pathnames* "dumbrebaseeditor.sh" legit-path)))) - (ensure-directories-exist legit-path) - (unless (uiop:file-exists-p script-path) - (str:to-file script-path *rebase-script-content*)) - ;; Ensure the file is executable. - #+unix - (uiop:run-program (list "chmod" "+x" script-path) - :output :string - :error-output :string - :ignore-error-status t) - #-unix - (porcelain-error "lem/legit: our rebase script is only for Unix platforms currently. We need to run a shell script and trap a signal.") - (setf *rebase-script-path* script-path)))) - -(defvar *rebase-pid* nil - "PID file for the git rebase in process.") -;; With this approach, only 1 rebase per Lem process. - -(defun root-commit-p (hash) - "Find this repository's very first commit on the current branch, - return T if this commit hash is the root. - - hash: (string) can be a short commit hash or an entire one. - - This check is required when doing a git interactive rebase." - ;; the git command - ;; git rebase --interactive a1b2c3^ - ;; fails if a1b2c3 is the root commit. - ;; We must use --root instead. - (let ((root (run-git (list "rev-list" - "--max-parents=0" - "HEAD")))) - ;; We use small hashes, so don't use equal. - (str:starts-with-p hash root))) - -(defun rebase-interactively (&key from) - (case *vcs* - (:git (git-rebase-interactively :from from)) - (:hg (porcelain-error "Interactive rebase not implemented for Mercurial")) - (:fossil (porcelain-error "No interactive rebase for Fossil.")) - (t (porcelain-error "Interactive rebase not available for this VCS: ~a" *vcs*)))) - -(defun git-rebase-interactively (&key from) - "Start a rebase session. - - Then edit the git rebase file and validate the rebase with `rebase-continue` - or stop it with `rebase-abort`. - - from: commit hash (string) to start the rebase from. - - Return three values suitable for `legit:run-function`: output string, error output string, exit code (integer)." - ;; For testing, go to a test project (,cd on Slime), and edit this project's - ;; .git/rebase-merge/git-rebase-merge-todo - ;; Beware of C-c ~ lol^^ - (when (uiop:directory-exists-p ".git/rebase-merge/") - (porcelain-error "It seems that there is already a rebase-merge directory, -and I wonder if you are in the middle of another rebase. -If that is the case, please try - git rebase (--continue | --abort | --skip) -If that is not the case, please - rm -fr \".git/rebase-merge\" -and run me again. -I am stopping in case you still have something valuable there.")) - - (unless from - (return-from git-rebase-interactively - (values "Git rebase is missing the commit to rebase from. We are too shy to rebase everything from the root commit yet. Aborting" - nil - 1))) - - (let ((editor (uiop:getenv "EDITOR"))) - (setf (uiop:getenv "EDITOR") (rebase-script-path)) - (unwind-protect - ;; xxx: get the error output, if any, to get explanations of failure. - (let ((process (uiop:launch-program (list - "git" - "rebase" - "--autostash" - "-i" - ;; Give the right commit to rebase from. - ;; When rebasing from the root commit, - ;; something special? - (if (root-commit-p from) - "--root" - (format nil "~a^" from))) - :output :stream - :error-output :stream - :ignore-error-status t))) - (if (uiop:process-alive-p process) - (let* ((output (read-line (uiop:process-info-output process))) - (pidtxt (str:trim (second (str:split ":" output))))) - (setf *rebase-pid* pidtxt) - (format t "The git interactive rebase is started on pid ~a. Edit the rebase file and validate." pidtxt) - (values (format nil "rebase started") - nil - 0)) - (porcelain-error "git rebase process didn't start properly. Aborting."))) - (setf (uiop:getenv "EDITOR") editor)))) - -(defun rebase-continue () - "Either send a continuation signal to the underlying git rebase process, for it to pick up our changes to the interactive rebase file, - either call git rebase --continue." - (case *vcs* - (:git (git-rebase-continue)) - (t - (porcelain-error "Rebasing is not supported for ~a" *vcs*)))) - -(defun %rebase-signal (&key (sig "-SIGTERM")) - "Send a kill signal to our rebase script: with -SIGTERM, git picks up our changes and continues the rebase process. This is called by a rebase continue command. - With -SIGKILL the process is stopped. This is called by rebase abort." - (multiple-value-bind (output error-output exit-code) - (uiop:run-program (list "kill" sig *rebase-pid*) - :output :string - :error-output :string - :ignore-error-status t) - (declare (ignorable output)) - (setf *rebase-pid* nil) - (values (format nil "rebase finished.") - error-output - exit-code))) - -(defun git-rebase-continue () - (cond - ;; First, if we are running our rebase script, send a "continue" signal - ;; so that git continues the rebase. - ;; This is used by C-c C-c in the interactive rebase buffer. - (*rebase-pid* - (%rebase-signal)) - ;; Check if a rebase was started by someone else and continue it. - ;; This is called from legit interace: "r" "c". - ((uiop:directory-exists-p ".git/rebase-merge/") - (run-git (list "rebase" "--continue"))) - (t - (porcelain-error "No git rebase in process?")))) - -(defun rebase-abort () - (case *vcs* - (:git (git-rebase-abort)) - (t - (porcelain-error "Rebasing is not supported for ~a" *vcs*)))) - -(defun git-rebase-abort () - (cond - ;; First, if we are running our rebase script, kill it. - ;; This makes git abort the rebase too. - ;; This is used by C-c C-k in the interactive rebase buffer. - (*rebase-pid* - (%rebase-signal :sig "-SIGKILL")) - ;; Check if a rebase was started by someone else and abort it. - ;; This is called from legit interface: "r" "a". - ((uiop:directory-exists-p ".git/rebase-merge/") - (run-git (list "rebase" "--abort"))) - (t - (porcelain-error "No git rebase in process? PID not found.")))) - -(defun rebase-skip () - (case *vcs* - (:git (git-rebase-skip)) - (t - (porcelain-error "Rebasing is not supported for ~a" *vcs*)))) - -(defun git-rebase-skip () - (cond - (*rebase-pid* - (porcelain-error "The rebase process you started from Lem is still running. Please continue or abort it (or kill job ~a)" *rebase-pid*)) - ;; Check if a rebase was started by someone else and abort it. - ;; This is called from legit interface: "r" "s". - ((uiop:directory-exists-p ".git/rebase-merge/") - (run-git (list "rebase" "--skip"))) - (t - (porcelain-error "No git rebase in process? PID not found.")))) +(defgeneric discard-file (vcs file) + (:documentation "Discard all changes to this file") + (:method (vcs file) + (declare (ignorable file)) + (porcelain-error "discard-file is not implemented for this VCS: ~a" (vcs-name vcs)))) + +(defgeneric apply-patch (vcs diff &key reverse) + (:documentation + "Apply a patch from this diff. + diff: string.") + (:method (vcs diff &key reverse) + (declare (ignorable diff reverse)) + (porcelain-error "lem/porcelain:apply-patch not implemented for vcs ~a" (vcs-name vcs)))) + +(defgeneric checkout (vcs branch) + (:documentation "Checkouts out branch (str) in vcs") + (:method (vcs branch) + (declare (ignorable branch)) + (porcelain-error "lem/porcelain:checkout not implemented for vcs ~a" (vcs-name vcs)))) + +(defgeneric checkout-create (vcs new start) + (:documentation "Checkouts out branch (str) in vcs, creating it at HEAD if it does not exit") + (:method (vcs new start) + (declare (ignorable vcs new start)) + (porcelain-error "lem/porcelain:checkout-create not implemented for vcs ~a" (vcs-name vcs)))) + +(defgeneric pull (vcs) + (:documentation "Pulls remotes") + (:method (vcs) + (porcelain-error "lem/porcelain:pull not implemented for vcs ~a" (vcs-name vcs)))) + +(defgeneric push (vcs) + (:documentation "Pushes to remotes") + (:method (vcs) + (porcelain-error "lem/porcelain:push not implemented for vcs ~a" (vcs-name vcs)))) + +;; Interactive rebase +(defgeneric rebase-interactively (vcs &key from) + (:method (vcs &key from) + (declare (ignorable from)) + (porcelain-error "lem/porcelain:rebase-interactively not implemented for vcs ~a" (vcs-name vcs)))) + +(defgeneric rebase-continue (vcs) + (:documentation + "Either send a continuation signal to the underlying git rebase process, for it to pick up our changes to the interactive rebase file, + either call git rebase --continue.") + (:method (vcs) + (porcelain-error "lem/porcelain:rebase-continue not implemented for vcs ~a" (vcs-name vcs)))) + +(defgeneric rebase-abort (vcs) + (:method (vcs) + (porcelain-error "lem/porcelain:rebase-abort not implemented for vcs ~a" (vcs-name vcs)))) + +(defgeneric rebase-skip (vcs) + (:method (vcs) + (porcelain-error "lem/porcelain:rebase-skip not implemented for vcs ~a" (vcs-name vcs)))) diff --git a/scripts/dumbrebaseeditor.sh b/extensions/legit/scripts/dumbrebaseeditor.sh similarity index 100% rename from scripts/dumbrebaseeditor.sh rename to extensions/legit/scripts/dumbrebaseeditor.sh diff --git a/extensions/lem-base16-themes/LICENSE b/extensions/lem-base16-themes/LICENSE new file mode 100644 index 000000000..38ad1f2ad --- /dev/null +++ b/extensions/lem-base16-themes/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022 Łukasz Pankowski + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/extensions/lem-base16-themes/README.md b/extensions/lem-base16-themes/README.md new file mode 100644 index 000000000..c66e3994e --- /dev/null +++ b/extensions/lem-base16-themes/README.md @@ -0,0 +1,38 @@ +# base16 themes for Lem editor/IDE + +## Installation + +To use base16 themes in Lem editor run + +``` +$ ros install lukpank/lem-base16-themes +``` + +then add to your `~/.lem/init.lisp` the following lines + +``` +(load-library "base16-themes") +(load-theme "espresso") +``` + +changing the theme `espresso` to the theme you want to use. + +You can also change the theme interactively with `M-x load-theme`. + +## Regenerating themes + +To regenerate `src/themes.lisp` run in slime REPL + +``` +(load "/path/to/lem-base16-themes/lem-base16-themes-generate.asd") +(ql:quickload "lem-base16-themes-generate") +(lem-base16-themes-generate:regenerate-themes) +``` + +## License + +Licensed under [MIT License](https://github.com/lukpank/lem-base16-themes/blob/master/LICENSE). + +## Author + +Łukasz Pankowski diff --git a/extensions/lem-base16-themes/lem-base16-themes-generate.asd b/extensions/lem-base16-themes/lem-base16-themes-generate.asd new file mode 100644 index 000000000..95571021d --- /dev/null +++ b/extensions/lem-base16-themes/lem-base16-themes-generate.asd @@ -0,0 +1,7 @@ +(asdf:defsystem "lem-base16-themes-generate" + :author "Łukasz Pankowski" + :license "MIT" + :depends-on ("cl-base16" "cl-mustache") + :components ((:module "src" + :components + ((:file "generate"))))) diff --git a/extensions/lem-base16-themes/lem-base16-themes.asd b/extensions/lem-base16-themes/lem-base16-themes.asd new file mode 100644 index 000000000..edf6b77b3 --- /dev/null +++ b/extensions/lem-base16-themes/lem-base16-themes.asd @@ -0,0 +1,9 @@ +(asdf:defsystem "lem-base16-themes" + :author "Łukasz Pankowski" + :license "MIT" + :depends-on (#:lem) + :components ((:module "src" + :components + ((:file "package") + (:file "macros") + (:file "themes" :depends-on ("macros")))))) diff --git a/extensions/lem-base16-themes/src/generate.lisp b/extensions/lem-base16-themes/src/generate.lisp new file mode 100644 index 000000000..2f49da2cb --- /dev/null +++ b/extensions/lem-base16-themes/src/generate.lisp @@ -0,0 +1,24 @@ +(in-package :cl-user) +(defpackage :lem-base16-themes-generate + (:use :common-lisp) + (:export + #:generate + #:regenerate-themes)) + +(in-package :lem-base16-themes-generate) + +(defparameter *base-dir* (directory-namestring (asdf:system-relative-pathname :lem-base16-themes-generate "lem-base16-themes.asd"))) + +(defun generate (out) + (format out ";;; AUTOGENERATED~%~%(in-package :lem-base16-themes)~%") + (let ((template (merge-pathnames "templates/default.mustache" *base-dir*))) + (loop :for scheme-dir :in (uiop:subdirectories (merge-pathnames "schemes/" cl-base16:*source-dir*)) + :do (unless (uiop:string-prefix-p "." (first (last (pathname-directory scheme-dir)))) + (loop :for scheme :in (uiop:directory-files scheme-dir) + :do (if (string= "yaml" (pathname-type scheme)) + (format out "~%~A" (mustache:render* template (cl-base16::load-scheme scheme))))))))) + +(defun regenerate-themes () + (with-open-file (out (merge-pathnames "src/themes.lisp" *base-dir*) + :direction :output :external-format :utf-8 :if-exists :supersede) + (generate out))) diff --git a/extensions/lem-base16-themes/src/macros.lisp b/extensions/lem-base16-themes/src/macros.lisp new file mode 100644 index 000000000..bc97e4075 --- /dev/null +++ b/extensions/lem-base16-themes/src/macros.lisp @@ -0,0 +1,76 @@ +(in-package :lem-base16-themes) + +(defparameter *light-threshold* 112) + +(defmacro define-base16-color-theme (name &body colors) + (labels ((color (name) (getf colors name)) + (rgb (color) (mapcar (lambda (i) (parse-integer (subseq color i (+ i 2)) :radix 16)) '(1 3 5))) + (gray (c) (+ (* 0.3 (nth 0 c)) (* 0.59 (nth 1 c)) (* 0.11 (nth 2 c)))) + (dark (color) (< (gray (rgb color)) *light-threshold*))) + `(lem:define-color-theme ,name () + (:display-background-mode ,(if (dark (color :base00)) :dark :light)) + (:foreground ,(color :base05)) + (:background ,(color :base00)) + (:inactive-window-background ,(color :base00)) + (lem:region :foreground nil :background ,(color :base02)) + (lem:syntax-warning-attribute :foreground ,(color :base08)) + (lem:syntax-string-attribute :foreground ,(color :base0B)) + (lem:syntax-comment-attribute :foreground ,(color :base03)) + (lem:syntax-keyword-attribute :foreground ,(color :base0E)) + (lem:syntax-constant-attribute :foreground ,(color :base09)) + (lem:syntax-function-name-attribute :foreground ,(color :base0D)) + (lem:syntax-variable-attribute :foreground ,(color :base08)) + (lem:syntax-type-attribute :foreground ,(color :base0A)) + (lem:syntax-builtin-attribute :foreground ,(color :base0C)) + + ;; Modeline + (lem:modeline :background ,(color :base02) :foreground ,(color :base07)) + (lem:modeline-inactive :background ,(color :base01) :foreground ,(color :base03)) + (lem:modeline-name-attribute :foreground ,(color :base09)) + (lem:inactive-modeline-name-attribute :foreground ,(color :base03)) + (lem:modeline-major-mode-attribute :foreground ,(color :base0D)) + (lem:inactive-modeline-major-mode-attribute :foreground ,(color :base03)) + (lem:modeline-minor-modes-attribute :foreground ,(color :base07)) + (lem:inactive-modeline-minor-modes-attribute :foreground ,(color :base03)) + (lem:modeline-position-attribute :foreground ,(color :base06) :background ,(color :base01)) + (lem:inactive-modeline-position-attribute :foreground ,(color :base03) :background ,(color :base01)) + (lem:modeline-posline-attribute :foreground ,(color :base00) :background ,(color :base05)) + (lem:inactive-modeline-posline-attribute :foreground ,(color :base00) :background ,(color :base02)) + + ;; Multiplexer + (lem/frame-multiplexer:frame-multiplexer-active-frame-name-attribute + :foreground ,(color :base01) :background ,(color :base0A) :bold t) + (lem/frame-multiplexer:frame-multiplexer-frame-name-attribute + :foreground ,(color :base01) :background ,(color :base04) :bold t) + (lem/frame-multiplexer:frame-multiplexer-background-attribute + :foreground ,(color :base0A) :background ,(color :base01)) + + ;; Paren coloring + (lem-lisp-mode/paren-coloring:paren-color-1 :foreground ,(color :base08)) + (lem-lisp-mode/paren-coloring:paren-color-2 :foreground ,(color :base09)) + (lem-lisp-mode/paren-coloring:paren-color-3 :foreground ,(color :base0A)) + (lem-lisp-mode/paren-coloring:paren-color-4 :foreground ,(color :base0B)) + (lem-lisp-mode/paren-coloring:paren-color-5 :foreground ,(color :base0C)) + (lem-lisp-mode/paren-coloring:paren-color-6 :foreground ,(color :base0D)) + + ;; Document + (lem:document-header1-attribute :foreground ,(color :base0D) :bold t) + (lem:document-header2-attribute :foreground ,(color :base0C) :bold t) + (lem:document-header3-attribute :foreground ,(color :base0B) :bold t) + (lem:document-header4-attribute :foreground ,(color :base0A) :bold t) + (lem:document-header5-attribute :foreground ,(color :base09) :bold t) + (lem:document-header6-attribute :foreground ,(color :base08) :bold t) + (lem:document-bold-attribute :bold t) + (lem:document-italic-attribute :foreground ,(color :base0E)) + (lem:document-underline-attribute :underline t) + (lem:document-link-attribute :foreground ,(color :base0D) :underline t) + (lem:document-list-attribute :foreground ,(color :base0C)) + (lem:document-code-block-attribute :background ,(color :base01) :foreground ,(color :base05)) + (lem:document-inline-code-attribute :background ,(color :base01) :foreground ,(color :base0F)) + (lem:document-blockquote-attribute :foreground ,(color :base03)) + (lem:document-table-attribute :foreground ,(color :base05) :background ,(color :base01)) + (lem:document-task-list-attribute :foreground ,(color :base0B)) + (lem:document-metadata-attribute :foreground ,(color :base0D)) + + ,@(loop :for (name color) :on colors :by #'cddr + :collect (list name color))))) diff --git a/extensions/lem-base16-themes/src/package.lisp b/extensions/lem-base16-themes/src/package.lisp new file mode 100644 index 000000000..3cc8913ed --- /dev/null +++ b/extensions/lem-base16-themes/src/package.lisp @@ -0,0 +1,3 @@ +(in-package :cl-user) +(defpackage :lem-base16-themes + (:use :common-lisp)) diff --git a/extensions/lem-base16-themes/src/themes.lisp b/extensions/lem-base16-themes/src/themes.lisp new file mode 100644 index 000000000..e92217d08 --- /dev/null +++ b/extensions/lem-base16-themes/src/themes.lisp @@ -0,0 +1,3888 @@ +;;; AUTOGENERATED + +(in-package :lem-base16-themes) + +;; Scheme: apprentice +;; Scheme author: romainl + +(define-base16-color-theme "apprentice" + :base00 "#262626" + :base01 "#303030" + :base02 "#333333" + :base03 "#6C6C6C" + :base04 "#787878" + :base05 "#BCBCBC" + :base06 "#C9C9C9" + :base07 "#FFFFFF" + :base08 "#5F8787" + :base09 "#FF8700" + :base0A "#5F8787" + :base0B "#87AF87" + :base0C "#5F875F" + :base0D "#FFFFAF" + :base0E "#87AFD7" + :base0F "#5F87AF") + +;; Scheme: atelier-cave-light +;; Scheme author: Bram de Haan (http://atelierbramdehaan.nl) + +(define-base16-color-theme "atelier-cave-light" + :base00 "#efecf4" + :base01 "#e2dfe7" + :base02 "#8b8792" + :base03 "#7e7887" + :base04 "#655f6d" + :base05 "#585260" + :base06 "#26232a" + :base07 "#19171c" + :base08 "#be4678" + :base09 "#aa573c" + :base0A "#a06e3b" + :base0B "#2a9292" + :base0C "#398bc6" + :base0D "#576ddb" + :base0E "#955ae7" + :base0F "#bf40bf") + +;; Scheme: atelier-cave +;; Scheme author: Bram de Haan (http://atelierbramdehaan.nl) + +(define-base16-color-theme "atelier-cave" + :base00 "#19171c" + :base01 "#26232a" + :base02 "#585260" + :base03 "#655f6d" + :base04 "#7e7887" + :base05 "#8b8792" + :base06 "#e2dfe7" + :base07 "#efecf4" + :base08 "#be4678" + :base09 "#aa573c" + :base0A "#a06e3b" + :base0B "#2a9292" + :base0C "#398bc6" + :base0D "#576ddb" + :base0E "#955ae7" + :base0F "#bf40bf") + +;; Scheme: atelier-dune-light +;; Scheme author: Bram de Haan (http://atelierbramdehaan.nl) + +(define-base16-color-theme "atelier-dune-light" + :base00 "#fefbec" + :base01 "#e8e4cf" + :base02 "#a6a28c" + :base03 "#999580" + :base04 "#7d7a68" + :base05 "#6e6b5e" + :base06 "#292824" + :base07 "#20201d" + :base08 "#d73737" + :base09 "#b65611" + :base0A "#ae9513" + :base0B "#60ac39" + :base0C "#1fad83" + :base0D "#6684e1" + :base0E "#b854d4" + :base0F "#d43552") + +;; Scheme: atelier-dune +;; Scheme author: Bram de Haan (http://atelierbramdehaan.nl) + +(define-base16-color-theme "atelier-dune" + :base00 "#20201d" + :base01 "#292824" + :base02 "#6e6b5e" + :base03 "#7d7a68" + :base04 "#999580" + :base05 "#a6a28c" + :base06 "#e8e4cf" + :base07 "#fefbec" + :base08 "#d73737" + :base09 "#b65611" + :base0A "#ae9513" + :base0B "#60ac39" + :base0C "#1fad83" + :base0D "#6684e1" + :base0E "#b854d4" + :base0F "#d43552") + +;; Scheme: atelier-estuary-light +;; Scheme author: Bram de Haan (http://atelierbramdehaan.nl) + +(define-base16-color-theme "atelier-estuary-light" + :base00 "#f4f3ec" + :base01 "#e7e6df" + :base02 "#929181" + :base03 "#878573" + :base04 "#6c6b5a" + :base05 "#5f5e4e" + :base06 "#302f27" + :base07 "#22221b" + :base08 "#ba6236" + :base09 "#ae7313" + :base0A "#a5980d" + :base0B "#7d9726" + :base0C "#5b9d48" + :base0D "#36a166" + :base0E "#5f9182" + :base0F "#9d6c7c") + +;; Scheme: atelier-estuary +;; Scheme author: Bram de Haan (http://atelierbramdehaan.nl) + +(define-base16-color-theme "atelier-estuary" + :base00 "#22221b" + :base01 "#302f27" + :base02 "#5f5e4e" + :base03 "#6c6b5a" + :base04 "#878573" + :base05 "#929181" + :base06 "#e7e6df" + :base07 "#f4f3ec" + :base08 "#ba6236" + :base09 "#ae7313" + :base0A "#a5980d" + :base0B "#7d9726" + :base0C "#5b9d48" + :base0D "#36a166" + :base0E "#5f9182" + :base0F "#9d6c7c") + +;; Scheme: atelier-forest-light +;; Scheme author: Bram de Haan (http://atelierbramdehaan.nl) + +(define-base16-color-theme "atelier-forest-light" + :base00 "#f1efee" + :base01 "#e6e2e0" + :base02 "#a8a19f" + :base03 "#9c9491" + :base04 "#766e6b" + :base05 "#68615e" + :base06 "#2c2421" + :base07 "#1b1918" + :base08 "#f22c40" + :base09 "#df5320" + :base0A "#c38418" + :base0B "#7b9726" + :base0C "#3d97b8" + :base0D "#407ee7" + :base0E "#6666ea" + :base0F "#c33ff3") + +;; Scheme: atelier-forest +;; Scheme author: Bram de Haan (http://atelierbramdehaan.nl) + +(define-base16-color-theme "atelier-forest" + :base00 "#1b1918" + :base01 "#2c2421" + :base02 "#68615e" + :base03 "#766e6b" + :base04 "#9c9491" + :base05 "#a8a19f" + :base06 "#e6e2e0" + :base07 "#f1efee" + :base08 "#f22c40" + :base09 "#df5320" + :base0A "#c38418" + :base0B "#7b9726" + :base0C "#3d97b8" + :base0D "#407ee7" + :base0E "#6666ea" + :base0F "#c33ff3") + +;; Scheme: atelier-heath-light +;; Scheme author: Bram de Haan (http://atelierbramdehaan.nl) + +(define-base16-color-theme "atelier-heath-light" + :base00 "#f7f3f7" + :base01 "#d8cad8" + :base02 "#ab9bab" + :base03 "#9e8f9e" + :base04 "#776977" + :base05 "#695d69" + :base06 "#292329" + :base07 "#1b181b" + :base08 "#ca402b" + :base09 "#a65926" + :base0A "#bb8a35" + :base0B "#918b3b" + :base0C "#159393" + :base0D "#516aec" + :base0E "#7b59c0" + :base0F "#cc33cc") + +;; Scheme: atelier-heath +;; Scheme author: Bram de Haan (http://atelierbramdehaan.nl) + +(define-base16-color-theme "atelier-heath" + :base00 "#1b181b" + :base01 "#292329" + :base02 "#695d69" + :base03 "#776977" + :base04 "#9e8f9e" + :base05 "#ab9bab" + :base06 "#d8cad8" + :base07 "#f7f3f7" + :base08 "#ca402b" + :base09 "#a65926" + :base0A "#bb8a35" + :base0B "#918b3b" + :base0C "#159393" + :base0D "#516aec" + :base0E "#7b59c0" + :base0F "#cc33cc") + +;; Scheme: atelier-lakeside-light +;; Scheme author: Bram de Haan (http://atelierbramdehaan.nl) + +(define-base16-color-theme "atelier-lakeside-light" + :base00 "#ebf8ff" + :base01 "#c1e4f6" + :base02 "#7ea2b4" + :base03 "#7195a8" + :base04 "#5a7b8c" + :base05 "#516d7b" + :base06 "#1f292e" + :base07 "#161b1d" + :base08 "#d22d72" + :base09 "#935c25" + :base0A "#8a8a0f" + :base0B "#568c3b" + :base0C "#2d8f6f" + :base0D "#257fad" + :base0E "#6b6bb8" + :base0F "#b72dd2") + +;; Scheme: atelier-lakeside +;; Scheme author: Bram de Haan (http://atelierbramdehaan.nl) + +(define-base16-color-theme "atelier-lakeside" + :base00 "#161b1d" + :base01 "#1f292e" + :base02 "#516d7b" + :base03 "#5a7b8c" + :base04 "#7195a8" + :base05 "#7ea2b4" + :base06 "#c1e4f6" + :base07 "#ebf8ff" + :base08 "#d22d72" + :base09 "#935c25" + :base0A "#8a8a0f" + :base0B "#568c3b" + :base0C "#2d8f6f" + :base0D "#257fad" + :base0E "#6b6bb8" + :base0F "#b72dd2") + +;; Scheme: atelier-plateau-light +;; Scheme author: Bram de Haan (http://atelierbramdehaan.nl) + +(define-base16-color-theme "atelier-plateau-light" + :base00 "#f4ecec" + :base01 "#e7dfdf" + :base02 "#8a8585" + :base03 "#7e7777" + :base04 "#655d5d" + :base05 "#585050" + :base06 "#292424" + :base07 "#1b1818" + :base08 "#ca4949" + :base09 "#b45a3c" + :base0A "#a06e3b" + :base0B "#4b8b8b" + :base0C "#5485b6" + :base0D "#7272ca" + :base0E "#8464c4" + :base0F "#bd5187") + +;; Scheme: atelier-plateau +;; Scheme author: Bram de Haan (http://atelierbramdehaan.nl) + +(define-base16-color-theme "atelier-plateau" + :base00 "#1b1818" + :base01 "#292424" + :base02 "#585050" + :base03 "#655d5d" + :base04 "#7e7777" + :base05 "#8a8585" + :base06 "#e7dfdf" + :base07 "#f4ecec" + :base08 "#ca4949" + :base09 "#b45a3c" + :base0A "#a06e3b" + :base0B "#4b8b8b" + :base0C "#5485b6" + :base0D "#7272ca" + :base0E "#8464c4" + :base0F "#bd5187") + +;; Scheme: atelier-savanna-light +;; Scheme author: Bram de Haan (http://atelierbramdehaan.nl) + +(define-base16-color-theme "atelier-savanna-light" + :base00 "#ecf4ee" + :base01 "#dfe7e2" + :base02 "#87928a" + :base03 "#78877d" + :base04 "#5f6d64" + :base05 "#526057" + :base06 "#232a25" + :base07 "#171c19" + :base08 "#b16139" + :base09 "#9f713c" + :base0A "#a07e3b" + :base0B "#489963" + :base0C "#1c9aa0" + :base0D "#478c90" + :base0E "#55859b" + :base0F "#867469") + +;; Scheme: atelier-savanna +;; Scheme author: Bram de Haan (http://atelierbramdehaan.nl) + +(define-base16-color-theme "atelier-savanna" + :base00 "#171c19" + :base01 "#232a25" + :base02 "#526057" + :base03 "#5f6d64" + :base04 "#78877d" + :base05 "#87928a" + :base06 "#dfe7e2" + :base07 "#ecf4ee" + :base08 "#b16139" + :base09 "#9f713c" + :base0A "#a07e3b" + :base0B "#489963" + :base0C "#1c9aa0" + :base0D "#478c90" + :base0E "#55859b" + :base0F "#867469") + +;; Scheme: atelier-seaside-light +;; Scheme author: Bram de Haan (http://atelierbramdehaan.nl) + +(define-base16-color-theme "atelier-seaside-light" + :base00 "#f4fbf4" + :base01 "#cfe8cf" + :base02 "#8ca68c" + :base03 "#809980" + :base04 "#687d68" + :base05 "#5e6e5e" + :base06 "#242924" + :base07 "#131513" + :base08 "#e6193c" + :base09 "#87711d" + :base0A "#98981b" + :base0B "#29a329" + :base0C "#1999b3" + :base0D "#3d62f5" + :base0E "#ad2bee" + :base0F "#e619c3") + +;; Scheme: atelier-seaside +;; Scheme author: Bram de Haan (http://atelierbramdehaan.nl) + +(define-base16-color-theme "atelier-seaside" + :base00 "#131513" + :base01 "#242924" + :base02 "#5e6e5e" + :base03 "#687d68" + :base04 "#809980" + :base05 "#8ca68c" + :base06 "#cfe8cf" + :base07 "#f4fbf4" + :base08 "#e6193c" + :base09 "#87711d" + :base0A "#98981b" + :base0B "#29a329" + :base0C "#1999b3" + :base0D "#3d62f5" + :base0E "#ad2bee" + :base0F "#e619c3") + +;; Scheme: atelier-sulphurpool-light +;; Scheme author: Bram de Haan (http://atelierbramdehaan.nl) + +(define-base16-color-theme "atelier-sulphurpool-light" + :base00 "#f5f7ff" + :base01 "#dfe2f1" + :base02 "#979db4" + :base03 "#898ea4" + :base04 "#6b7394" + :base05 "#5e6687" + :base06 "#293256" + :base07 "#202746" + :base08 "#c94922" + :base09 "#c76b29" + :base0A "#c08b30" + :base0B "#ac9739" + :base0C "#22a2c9" + :base0D "#3d8fd1" + :base0E "#6679cc" + :base0F "#9c637a") + +;; Scheme: atelier-sulphurpool +;; Scheme author: Bram de Haan (http://atelierbramdehaan.nl) + +(define-base16-color-theme "atelier-sulphurpool" + :base00 "#202746" + :base01 "#293256" + :base02 "#5e6687" + :base03 "#6b7394" + :base04 "#898ea4" + :base05 "#979db4" + :base06 "#dfe2f1" + :base07 "#f5f7ff" + :base08 "#c94922" + :base09 "#c76b29" + :base0A "#c08b30" + :base0B "#ac9739" + :base0C "#22a2c9" + :base0D "#3d8fd1" + :base0E "#6679cc" + :base0F "#9c637a") + +;; Scheme: atlas +;; Scheme author: Alex Lende (https://ajlende.com) + +(define-base16-color-theme "atlas" + :base00 "#002635" + :base01 "#00384d" + :base02 "#517F8D" + :base03 "#6C8B91" + :base04 "#869696" + :base05 "#a1a19a" + :base06 "#e6e6dc" + :base07 "#fafaf8" + :base08 "#ff5a67" + :base09 "#f08e48" + :base0A "#ffcc1b" + :base0B "#7fc06e" + :base0C "#14747e" + :base0D "#5dd7b9" + :base0E "#9a70a4" + :base0F "#c43060") + +;; Scheme: black-metal-bathory +;; Scheme author: metalelf0 (https://github.com/metalelf0) + +(define-base16-color-theme "black-metal-bathory" + :base00 "#000000" + :base01 "#121212" + :base02 "#222222" + :base03 "#333333" + :base04 "#999999" + :base05 "#c1c1c1" + :base06 "#999999" + :base07 "#c1c1c1" + :base08 "#5f8787" + :base09 "#aaaaaa" + :base0A "#e78a53" + :base0B "#fbcb97" + :base0C "#aaaaaa" + :base0D "#888888" + :base0E "#999999" + :base0F "#444444") + +;; Scheme: black-metal-burzum +;; Scheme author: metalelf0 (https://github.com/metalelf0) + +(define-base16-color-theme "black-metal-burzum" + :base00 "#000000" + :base01 "#121212" + :base02 "#222222" + :base03 "#333333" + :base04 "#999999" + :base05 "#c1c1c1" + :base06 "#999999" + :base07 "#c1c1c1" + :base08 "#5f8787" + :base09 "#aaaaaa" + :base0A "#99bbaa" + :base0B "#ddeecc" + :base0C "#aaaaaa" + :base0D "#888888" + :base0E "#999999" + :base0F "#444444") + +;; Scheme: black-metal-dark-funeral +;; Scheme author: metalelf0 (https://github.com/metalelf0) + +(define-base16-color-theme "black-metal-dark-funeral" + :base00 "#000000" + :base01 "#121212" + :base02 "#222222" + :base03 "#333333" + :base04 "#999999" + :base05 "#c1c1c1" + :base06 "#999999" + :base07 "#c1c1c1" + :base08 "#5f8787" + :base09 "#aaaaaa" + :base0A "#5f81a5" + :base0B "#d0dfee" + :base0C "#aaaaaa" + :base0D "#888888" + :base0E "#999999" + :base0F "#444444") + +;; Scheme: black-metal-gorgoroth +;; Scheme author: metalelf0 (https://github.com/metalelf0) + +(define-base16-color-theme "black-metal-gorgoroth" + :base00 "#000000" + :base01 "#121212" + :base02 "#222222" + :base03 "#333333" + :base04 "#999999" + :base05 "#c1c1c1" + :base06 "#999999" + :base07 "#c1c1c1" + :base08 "#5f8787" + :base09 "#aaaaaa" + :base0A "#8c7f70" + :base0B "#9b8d7f" + :base0C "#aaaaaa" + :base0D "#888888" + :base0E "#999999" + :base0F "#444444") + +;; Scheme: black-metal-immortal +;; Scheme author: metalelf0 (https://github.com/metalelf0) + +(define-base16-color-theme "black-metal-immortal" + :base00 "#000000" + :base01 "#121212" + :base02 "#222222" + :base03 "#333333" + :base04 "#999999" + :base05 "#c1c1c1" + :base06 "#999999" + :base07 "#c1c1c1" + :base08 "#5f8787" + :base09 "#aaaaaa" + :base0A "#556677" + :base0B "#7799bb" + :base0C "#aaaaaa" + :base0D "#888888" + :base0E "#999999" + :base0F "#444444") + +;; Scheme: black-metal-khold +;; Scheme author: metalelf0 (https://github.com/metalelf0) + +(define-base16-color-theme "black-metal-khold" + :base00 "#000000" + :base01 "#121212" + :base02 "#222222" + :base03 "#333333" + :base04 "#999999" + :base05 "#c1c1c1" + :base06 "#999999" + :base07 "#c1c1c1" + :base08 "#5f8787" + :base09 "#aaaaaa" + :base0A "#974b46" + :base0B "#eceee3" + :base0C "#aaaaaa" + :base0D "#888888" + :base0E "#999999" + :base0F "#444444") + +;; Scheme: black-metal-marduk +;; Scheme author: metalelf0 (https://github.com/metalelf0) + +(define-base16-color-theme "black-metal-marduk" + :base00 "#000000" + :base01 "#121212" + :base02 "#222222" + :base03 "#333333" + :base04 "#999999" + :base05 "#c1c1c1" + :base06 "#999999" + :base07 "#c1c1c1" + :base08 "#5f8787" + :base09 "#aaaaaa" + :base0A "#626b67" + :base0B "#a5aaa7" + :base0C "#aaaaaa" + :base0D "#888888" + :base0E "#999999" + :base0F "#444444") + +;; Scheme: black-metal-mayhem +;; Scheme author: metalelf0 (https://github.com/metalelf0) + +(define-base16-color-theme "black-metal-mayhem" + :base00 "#000000" + :base01 "#121212" + :base02 "#222222" + :base03 "#333333" + :base04 "#999999" + :base05 "#c1c1c1" + :base06 "#999999" + :base07 "#c1c1c1" + :base08 "#5f8787" + :base09 "#aaaaaa" + :base0A "#eecc6c" + :base0B "#f3ecd4" + :base0C "#aaaaaa" + :base0D "#888888" + :base0E "#999999" + :base0F "#444444") + +;; Scheme: black-metal-nile +;; Scheme author: metalelf0 (https://github.com/metalelf0) + +(define-base16-color-theme "black-metal-nile" + :base00 "#000000" + :base01 "#121212" + :base02 "#222222" + :base03 "#333333" + :base04 "#999999" + :base05 "#c1c1c1" + :base06 "#999999" + :base07 "#c1c1c1" + :base08 "#5f8787" + :base09 "#aaaaaa" + :base0A "#777755" + :base0B "#aa9988" + :base0C "#aaaaaa" + :base0D "#888888" + :base0E "#999999" + :base0F "#444444") + +;; Scheme: black-metal-venom +;; Scheme author: metalelf0 (https://github.com/metalelf0) + +(define-base16-color-theme "black-metal-venom" + :base00 "#000000" + :base01 "#121212" + :base02 "#222222" + :base03 "#333333" + :base04 "#999999" + :base05 "#c1c1c1" + :base06 "#999999" + :base07 "#c1c1c1" + :base08 "#5f8787" + :base09 "#aaaaaa" + :base0A "#79241f" + :base0B "#f8f7f2" + :base0C "#aaaaaa" + :base0D "#888888" + :base0E "#999999" + :base0F "#444444") + +;; Scheme: black-metal +;; Scheme author: metalelf0 (https://github.com/metalelf0) + +(define-base16-color-theme "black-metal" + :base00 "#000000" + :base01 "#121212" + :base02 "#222222" + :base03 "#333333" + :base04 "#999999" + :base05 "#c1c1c1" + :base06 "#999999" + :base07 "#c1c1c1" + :base08 "#5f8787" + :base09 "#aaaaaa" + :base0A "#a06666" + :base0B "#dd9999" + :base0C "#aaaaaa" + :base0D "#888888" + :base0E "#999999" + :base0F "#444444") + +;; Scheme: brogrammer +;; Scheme author: Vik Ramanujam (http://github.com/piggyslasher) + +(define-base16-color-theme "brogrammer" + :base00 "#1f1f1f" + :base01 "#f81118" + :base02 "#2dc55e" + :base03 "#ecba0f" + :base04 "#2a84d2" + :base05 "#4e5ab7" + :base06 "#1081d6" + :base07 "#d6dbe5" + :base08 "#d6dbe5" + :base09 "#de352e" + :base0A "#1dd361" + :base0B "#f3bd09" + :base0C "#1081d6" + :base0D "#5350b9" + :base0E "#0f7ddb" + :base0F "#ffffff") + +;; Scheme: brushtrees-dark +;; Scheme author: Abraham White <abelincoln.white@gmail.com> + +(define-base16-color-theme "brushtrees-dark" + :base00 "#485867" + :base01 "#5A6D7A" + :base02 "#6D828E" + :base03 "#8299A1" + :base04 "#98AFB5" + :base05 "#B0C5C8" + :base06 "#C9DBDC" + :base07 "#E3EFEF" + :base08 "#b38686" + :base09 "#d8bba2" + :base0A "#aab386" + :base0B "#87b386" + :base0C "#86b3b3" + :base0D "#868cb3" + :base0E "#b386b2" + :base0F "#b39f9f") + +;; Scheme: brushtrees +;; Scheme author: Abraham White <abelincoln.white@gmail.com> + +(define-base16-color-theme "brushtrees" + :base00 "#E3EFEF" + :base01 "#C9DBDC" + :base02 "#B0C5C8" + :base03 "#98AFB5" + :base04 "#8299A1" + :base05 "#6D828E" + :base06 "#5A6D7A" + :base07 "#485867" + :base08 "#b38686" + :base09 "#d8bba2" + :base0A "#aab386" + :base0B "#87b386" + :base0C "#86b3b3" + :base0D "#868cb3" + :base0E "#b386b2" + :base0F "#b39f9f") + +;; Scheme: circus +;; Scheme author: Stephan Boyer (https://github.com/stepchowfun) and Esther Wang (https://github.com/ewang12) + +(define-base16-color-theme "circus" + :base00 "#191919" + :base01 "#202020" + :base02 "#303030" + :base03 "#5f5a60" + :base04 "#505050" + :base05 "#a7a7a7" + :base06 "#808080" + :base07 "#ffffff" + :base08 "#dc657d" + :base09 "#4bb1a7" + :base0A "#c3ba63" + :base0B "#84b97c" + :base0C "#4bb1a7" + :base0D "#639ee4" + :base0E "#b888e2" + :base0F "#b888e2") + +;; Scheme: classic-dark +;; Scheme author: Jason Heeris (http://heeris.id.au) + +(define-base16-color-theme "classic-dark" + :base00 "#151515" + :base01 "#202020" + :base02 "#303030" + :base03 "#505050" + :base04 "#B0B0B0" + :base05 "#D0D0D0" + :base06 "#E0E0E0" + :base07 "#F5F5F5" + :base08 "#AC4142" + :base09 "#D28445" + :base0A "#F4BF75" + :base0B "#90A959" + :base0C "#75B5AA" + :base0D "#6A9FB5" + :base0E "#AA759F" + :base0F "#8F5536") + +;; Scheme: classic-light +;; Scheme author: Jason Heeris (http://heeris.id.au) + +(define-base16-color-theme "classic-light" + :base00 "#F5F5F5" + :base01 "#E0E0E0" + :base02 "#D0D0D0" + :base03 "#B0B0B0" + :base04 "#505050" + :base05 "#303030" + :base06 "#202020" + :base07 "#151515" + :base08 "#AC4142" + :base09 "#D28445" + :base0A "#F4BF75" + :base0B "#90A959" + :base0C "#75B5AA" + :base0D "#6A9FB5" + :base0E "#AA759F" + :base0F "#8F5536") + +;; Scheme: codeschool +;; Scheme author: blockloop + +(define-base16-color-theme "codeschool" + :base00 "#232c31" + :base01 "#1c3657" + :base02 "#2a343a" + :base03 "#3f4944" + :base04 "#84898c" + :base05 "#9ea7a6" + :base06 "#a7cfa3" + :base07 "#b5d8f6" + :base08 "#2a5491" + :base09 "#43820d" + :base0A "#a03b1e" + :base0B "#237986" + :base0C "#b02f30" + :base0D "#484d79" + :base0E "#c59820" + :base0F "#c98344") + +;; Scheme: colors +;; Scheme author: mrmrs (http://clrs.cc) + +(define-base16-color-theme "colors" + :base00 "#111111" + :base01 "#333333" + :base02 "#555555" + :base03 "#777777" + :base04 "#999999" + :base05 "#bbbbbb" + :base06 "#dddddd" + :base07 "#ffffff" + :base08 "#ff4136" + :base09 "#ff851b" + :base0A "#ffdc00" + :base0B "#2ecc40" + :base0C "#7fdbff" + :base0D "#0074d9" + :base0E "#b10dc9" + :base0F "#85144b") + +;; Scheme: cupertino +;; Scheme author: Defman21 + +(define-base16-color-theme "cupertino" + :base00 "#ffffff" + :base01 "#c0c0c0" + :base02 "#c0c0c0" + :base03 "#808080" + :base04 "#808080" + :base05 "#404040" + :base06 "#404040" + :base07 "#5e5e5e" + :base08 "#c41a15" + :base09 "#eb8500" + :base0A "#826b28" + :base0B "#007400" + :base0C "#318495" + :base0D "#0000ff" + :base0E "#a90d91" + :base0F "#826b28") + +;; Scheme: danqing-light +;; Scheme author: Wenhan Zhu (Cosmos) (zhuwenhan950913@gmail.com) + +(define-base16-color-theme "danqing-light" + :base00 "#fcfefd" + :base01 "#ecf6f2" + :base02 "#e0f0eF" + :base03 "#cad8d2" + :base04 "#9da8a3" + :base05 "#5a605d" + :base06 "#434846" + :base07 "#2d302f" + :base08 "#F9906F" + :base09 "#B38A61" + :base0A "#F0C239" + :base0B "#8AB361" + :base0C "#30DFF3" + :base0D "#B0A4E3" + :base0E "#CCA4E3" + :base0F "#CA6924") + +;; Scheme: danqing +;; Scheme author: Wenhan Zhu (Cosmos) (zhuwenhan950913@gmail.com) + +(define-base16-color-theme "danqing" + :base00 "#2d302f" + :base01 "#434846" + :base02 "#5a605d" + :base03 "#9da8a3" + :base04 "#cad8d2" + :base05 "#e0f0eF" + :base06 "#ecf6f2" + :base07 "#fcfefd" + :base08 "#F9906F" + :base09 "#B38A61" + :base0A "#F0C239" + :base0B "#8AB361" + :base0C "#30DFF3" + :base0D "#B0A4E3" + :base0E "#CCA4E3" + :base0F "#CA6924") + +;; Scheme: darcula +;; Scheme author: jetbrains + +(define-base16-color-theme "darcula" + :base00 "#2b2b2b" + :base01 "#323232" + :base02 "#323232" + :base03 "#606366" + :base04 "#a4a3a3" + :base05 "#a9b7c6" + :base06 "#ffc66d" + :base07 "#ffffff" + :base08 "#4eade5" + :base09 "#689757" + :base0A "#bbb529" + :base0B "#6a8759" + :base0C "#629755" + :base0D "#9876aa" + :base0E "#cc7832" + :base0F "#808080") + +;; Scheme: darkmoss +;; Scheme author: Gabriel Avanzi (https://github.com/avanzzzi) + +(define-base16-color-theme "darkmoss" + :base00 "#171e1f" + :base01 "#252c2d" + :base02 "#373c3d" + :base03 "#555e5f" + :base04 "#818f80" + :base05 "#c7c7a5" + :base06 "#e3e3c8" + :base07 "#e1eaef" + :base08 "#ff4658" + :base09 "#e6db74" + :base0A "#fdb11f" + :base0B "#499180" + :base0C "#66d9ef" + :base0D "#498091" + :base0E "#9bc0c8" + :base0F "#d27b53") + +;; Scheme: darkviolet +;; Scheme author: ruler501 (https://github.com/ruler501/base16-darkviolet) + +(define-base16-color-theme "darkviolet" + :base00 "#000000" + :base01 "#231a40" + :base02 "#432d59" + :base03 "#593380" + :base04 "#00ff00" + :base05 "#b08ae6" + :base06 "#9045e6" + :base07 "#a366ff" + :base08 "#a82ee6" + :base09 "#bb66cc" + :base0A "#f29df2" + :base0B "#4595e6" + :base0C "#40dfff" + :base0D "#4136d9" + :base0E "#7e5ce6" + :base0F "#a886bf") + +;; Scheme: cupcake +;; Scheme author: Chris Kempson (http://chriskempson.com) + +(define-base16-color-theme "cupcake" + :base00 "#fbf1f2" + :base01 "#f2f1f4" + :base02 "#d8d5dd" + :base03 "#bfb9c6" + :base04 "#a59daf" + :base05 "#8b8198" + :base06 "#72677E" + :base07 "#585062" + :base08 "#D57E85" + :base09 "#EBB790" + :base0A "#DCB16C" + :base0B "#A3B367" + :base0C "#69A9A7" + :base0D "#7297B9" + :base0E "#BB99B4" + :base0F "#BAA58C") + +;; Scheme: default-dark +;; Scheme author: Chris Kempson (http://chriskempson.com) + +(define-base16-color-theme "default-dark" + :base00 "#181818" + :base01 "#282828" + :base02 "#383838" + :base03 "#585858" + :base04 "#b8b8b8" + :base05 "#d8d8d8" + :base06 "#e8e8e8" + :base07 "#f8f8f8" + :base08 "#ab4642" + :base09 "#dc9656" + :base0A "#f7ca88" + :base0B "#a1b56c" + :base0C "#86c1b9" + :base0D "#7cafc2" + :base0E "#ba8baf" + :base0F "#a16946") + +;; Scheme: default-light +;; Scheme author: Chris Kempson (http://chriskempson.com) + +(define-base16-color-theme "default-light" + :base00 "#f8f8f8" + :base01 "#e8e8e8" + :base02 "#d8d8d8" + :base03 "#b8b8b8" + :base04 "#585858" + :base05 "#383838" + :base06 "#282828" + :base07 "#181818" + :base08 "#ab4642" + :base09 "#dc9656" + :base0A "#f7ca88" + :base0B "#a1b56c" + :base0C "#86c1b9" + :base0D "#7cafc2" + :base0E "#ba8baf" + :base0F "#a16946") + +;; Scheme: eighties +;; Scheme author: Chris Kempson (http://chriskempson.com) + +(define-base16-color-theme "eighties" + :base00 "#2d2d2d" + :base01 "#393939" + :base02 "#515151" + :base03 "#747369" + :base04 "#a09f93" + :base05 "#d3d0c8" + :base06 "#e8e6df" + :base07 "#f2f0ec" + :base08 "#f2777a" + :base09 "#f99157" + :base0A "#ffcc66" + :base0B "#99cc99" + :base0C "#66cccc" + :base0D "#6699cc" + :base0E "#cc99cc" + :base0F "#d27b53") + +;; Scheme: mocha +;; Scheme author: Chris Kempson (http://chriskempson.com) + +(define-base16-color-theme "mocha" + :base00 "#3B3228" + :base01 "#534636" + :base02 "#645240" + :base03 "#7e705a" + :base04 "#b8afad" + :base05 "#d0c8c6" + :base06 "#e9e1dd" + :base07 "#f5eeeb" + :base08 "#cb6077" + :base09 "#d28b71" + :base0A "#f4bc87" + :base0B "#beb55b" + :base0C "#7bbda4" + :base0D "#8ab3b5" + :base0E "#a89bb9" + :base0F "#bb9584") + +;; Scheme: ocean +;; Scheme author: Chris Kempson (http://chriskempson.com) + +(define-base16-color-theme "ocean" + :base00 "#2b303b" + :base01 "#343d46" + :base02 "#4f5b66" + :base03 "#65737e" + :base04 "#a7adba" + :base05 "#c0c5ce" + :base06 "#dfe1e8" + :base07 "#eff1f5" + :base08 "#bf616a" + :base09 "#d08770" + :base0A "#ebcb8b" + :base0B "#a3be8c" + :base0C "#96b5b4" + :base0D "#8fa1b3" + :base0E "#b48ead" + :base0F "#ab7967") + +;; Scheme: dirtysea +;; Scheme author: Kahlil (Kal) Hodgson + +(define-base16-color-theme "dirtysea" + :base00 "#e0e0e0" + :base01 "#d0dad0" + :base02 "#d0d0d0" + :base03 "#707070" + :base04 "#202020" + :base05 "#000000" + :base06 "#f8f8f8" + :base07 "#c4d9c4" + :base08 "#840000" + :base09 "#006565" + :base0A "#755B00" + :base0B "#730073" + :base0C "#755B00" + :base0D "#007300" + :base0E "#000090" + :base0F "#755B00") + +;; Scheme: dracula +;; Scheme author: Mike Barkmin (http://github.com/mikebarkmin) based on Dracula Theme (http://github.com/dracula) + +(define-base16-color-theme "dracula" + :base00 "#282936" + :base01 "#3a3c4e" + :base02 "#4d4f68" + :base03 "#626483" + :base04 "#62d6e8" + :base05 "#e9e9f4" + :base06 "#f1f2f8" + :base07 "#f7f7fb" + :base08 "#ea51b2" + :base09 "#b45bcf" + :base0A "#00f769" + :base0B "#ebff87" + :base0C "#a1efe4" + :base0D "#62d6e8" + :base0E "#b45bcf" + :base0F "#00f769") + +;; Scheme: edge-dark +;; Scheme author: cjayross (https://github.com/cjayross) + +(define-base16-color-theme "edge-dark" + :base00 "#262729" + :base01 "#88909f" + :base02 "#b7bec9" + :base03 "#3e4249" + :base04 "#73b3e7" + :base05 "#b7bec9" + :base06 "#d390e7" + :base07 "#3e4249" + :base08 "#e77171" + :base09 "#e77171" + :base0A "#dbb774" + :base0B "#a1bf78" + :base0C "#5ebaa5" + :base0D "#73b3e7" + :base0E "#d390e7" + :base0F "#5ebaa5") + +;; Scheme: edge-light +;; Scheme author: cjayross (https://github.com/cjayross) + +(define-base16-color-theme "edge-light" + :base00 "#fafafa" + :base01 "#7c9f4b" + :base02 "#d69822" + :base03 "#5e646f" + :base04 "#6587bf" + :base05 "#5e646f" + :base06 "#b870ce" + :base07 "#5e646f" + :base08 "#db7070" + :base09 "#db7070" + :base0A "#d69822" + :base0B "#7c9f4b" + :base0C "#509c93" + :base0D "#6587bf" + :base0E "#b870ce" + :base0F "#509c93") + +;; Scheme: equilibrium-dark +;; Scheme author: Carlo Abelli + +(define-base16-color-theme "equilibrium-dark" + :base00 "#0c1118" + :base01 "#181c22" + :base02 "#22262d" + :base03 "#7b776e" + :base04 "#949088" + :base05 "#afaba2" + :base06 "#cac6bd" + :base07 "#e7e2d9" + :base08 "#f04339" + :base09 "#df5923" + :base0A "#bb8801" + :base0B "#7f8b00" + :base0C "#00948b" + :base0D "#008dd1" + :base0E "#6a7fd2" + :base0F "#e3488e") + +;; Scheme: equilibrium-gray-dark +;; Scheme author: Carlo Abelli + +(define-base16-color-theme "equilibrium-gray-dark" + :base00 "#111111" + :base01 "#1b1b1b" + :base02 "#262626" + :base03 "#777777" + :base04 "#919191" + :base05 "#ababab" + :base06 "#c6c6c6" + :base07 "#e2e2e2" + :base08 "#f04339" + :base09 "#df5923" + :base0A "#bb8801" + :base0B "#7f8b00" + :base0C "#00948b" + :base0D "#008dd1" + :base0E "#6a7fd2" + :base0F "#e3488e") + +;; Scheme: equilibrium-gray-light +;; Scheme author: Carlo Abelli + +(define-base16-color-theme "equilibrium-gray-light" + :base00 "#f1f1f1" + :base01 "#e2e2e2" + :base02 "#d4d4d4" + :base03 "#777777" + :base04 "#5e5e5e" + :base05 "#474747" + :base06 "#303030" + :base07 "#1b1b1b" + :base08 "#d02023" + :base09 "#bf3e05" + :base0A "#9d6f00" + :base0B "#637200" + :base0C "#007a72" + :base0D "#0073b5" + :base0E "#4e66b6" + :base0F "#c42775") + +;; Scheme: equilibrium-light +;; Scheme author: Carlo Abelli + +(define-base16-color-theme "equilibrium-light" + :base00 "#f5f0e7" + :base01 "#e7e2d9" + :base02 "#d8d4cb" + :base03 "#73777f" + :base04 "#5a5f66" + :base05 "#43474e" + :base06 "#2c3138" + :base07 "#181c22" + :base08 "#d02023" + :base09 "#bf3e05" + :base0A "#9d6f00" + :base0B "#637200" + :base0C "#007a72" + :base0D "#0073b5" + :base0E "#4e66b6" + :base0F "#c42775") + +;; Scheme: decaf +;; Scheme author: Alex Mirrington (https://github.com/alexmirrington) + +(define-base16-color-theme "decaf" + :base00 "#2d2d2d" + :base01 "#393939" + :base02 "#515151" + :base03 "#777777" + :base04 "#b4b7b4" + :base05 "#cccccc" + :base06 "#e0e0e0" + :base07 "#ffffff" + :base08 "#ff7f7b" + :base09 "#ffbf70" + :base0A "#ffd67c" + :base0B "#beda78" + :base0C "#bed6ff" + :base0D "#90bee1" + :base0E "#efb3f7" + :base0F "#ff93b3") + +;; Scheme: espresso +;; Scheme author: Unknown. Maintained by Alex Mirrington (https://github.com/alexmirrington) + +(define-base16-color-theme "espresso" + :base00 "#2d2d2d" + :base01 "#393939" + :base02 "#515151" + :base03 "#777777" + :base04 "#b4b7b4" + :base05 "#cccccc" + :base06 "#e0e0e0" + :base07 "#ffffff" + :base08 "#d25252" + :base09 "#f9a959" + :base0A "#ffc66d" + :base0B "#a5c261" + :base0C "#bed6ff" + :base0D "#6c99bb" + :base0E "#d197d9" + :base0F "#f97394") + +;; Scheme: eva-dim +;; Scheme author: kjakapat (https://github.com/kjakapat) + +(define-base16-color-theme "eva-dim" + :base00 "#2a3b4d" + :base01 "#3d566f" + :base02 "#4b6988" + :base03 "#55799c" + :base04 "#7e90a3" + :base05 "#9fa2a6" + :base06 "#d6d7d9" + :base07 "#ffffff" + :base08 "#c4676c" + :base09 "#ff9966" + :base0A "#cfd05d" + :base0B "#5de561" + :base0C "#4b8f77" + :base0D "#1ae1dc" + :base0E "#9c6cd3" + :base0F "#bb64a9") + +;; Scheme: eva +;; Scheme author: kjakapat (https://github.com/kjakapat) + +(define-base16-color-theme "eva" + :base00 "#2a3b4d" + :base01 "#3d566f" + :base02 "#4b6988" + :base03 "#55799c" + :base04 "#7e90a3" + :base05 "#9fa2a6" + :base06 "#d6d7d9" + :base07 "#ffffff" + :base08 "#c4676c" + :base09 "#ff9966" + :base0A "#ffff66" + :base0B "#66ff66" + :base0C "#4b8f77" + :base0D "#15f4ee" + :base0E "#9c6cd3" + :base0F "#bb64a9") + +;; Scheme: everforest-dark +;; Scheme author: Sainnhe Park (sainnhe@gmail.com) + +(define-base16-color-theme "everforest-dark" + :base00 "#2b3339" + :base01 "#323c41" + :base02 "#383f45" + :base03 "#868d80" + :base04 "#d3c6aa" + :base05 "#d3c6aa" + :base06 "#e9e8d2" + :base07 "#fff9e8" + :base08 "#7fbbb3" + :base09 "#d699b6" + :base0A "#83c092" + :base0B "#dbbc7f" + :base0C "#e69875" + :base0D "#a7c080" + :base0E "#e67e80" + :base0F "#d699b6") + +;; Scheme: framer +;; Scheme author: Framer (Maintained by Jesse Hoyos) + +(define-base16-color-theme "framer" + :base00 "#181818" + :base01 "#151515" + :base02 "#464646" + :base03 "#747474" + :base04 "#B9B9B9" + :base05 "#D0D0D0" + :base06 "#E8E8E8" + :base07 "#EEEEEE" + :base08 "#FD886B" + :base09 "#FC4769" + :base0A "#FECB6E" + :base0B "#32CCDC" + :base0C "#ACDDFD" + :base0D "#20BCFC" + :base0E "#BA8CFC" + :base0F "#B15F4A") + +;; Scheme: fruit-soda +;; Scheme author: jozip + +(define-base16-color-theme "fruit-soda" + :base00 "#f1ecf1" + :base01 "#e0dee0" + :base02 "#d8d5d5" + :base03 "#b5b4b6" + :base04 "#979598" + :base05 "#515151" + :base06 "#474545" + :base07 "#2d2c2c" + :base08 "#fe3e31" + :base09 "#fe6d08" + :base0A "#f7e203" + :base0B "#47f74c" + :base0C "#0f9cfd" + :base0D "#2931df" + :base0E "#611fce" + :base0F "#b16f40") + +;; Scheme: gigavolt +;; Scheme author: Aidan Swope (http://github.com/Whillikers) + +(define-base16-color-theme "gigavolt" + :base00 "#202126" + :base01 "#2d303d" + :base02 "#5a576e" + :base03 "#a1d2e6" + :base04 "#cad3ff" + :base05 "#e9e7e1" + :base06 "#eff0f9" + :base07 "#f2fbff" + :base08 "#ff661a" + :base09 "#19f988" + :base0A "#ffdc2d" + :base0B "#f2e6a9" + :base0C "#fb6acb" + :base0D "#40bfff" + :base0E "#ae94f9" + :base0F "#6187ff") + +;; Scheme: github +;; Scheme author: Defman21 + +(define-base16-color-theme "github" + :base00 "#ffffff" + :base01 "#f5f5f5" + :base02 "#c8c8fa" + :base03 "#969896" + :base04 "#e8e8e8" + :base05 "#333333" + :base06 "#ffffff" + :base07 "#ffffff" + :base08 "#ed6a43" + :base09 "#0086b3" + :base0A "#795da3" + :base0B "#183691" + :base0C "#183691" + :base0D "#795da3" + :base0E "#a71d5d" + :base0F "#333333") + +;; Scheme: gruber +;; Scheme author: Patel, Nimai <nimai.m.patel@gmail.com>, colors from www.github.com/rexim/gruber-darker-theme + +(define-base16-color-theme "gruber" + :base00 "#181818" + :base01 "#453d41" + :base02 "#665c7f" + :base03 "#9dae93" + :base04 "#e4e4ef" + :base05 "#f4f4ff" + :base06 "#f5f5f5" + :base07 "#e4e4ef" + :base08 "#f43841" + :base09 "#c73c3f" + :base0A "#ffdd33" + :base0B "#73c936" + :base0C "#95a99f" + :base0D "#96a6c8" + :base0E "#9e95c7" + :base0F "#cc8c3c") + +;; Scheme: gruvbox-dark-hard +;; Scheme author: Dawid Kurek (dawikur@gmail.com), morhetz (https://github.com/morhetz/gruvbox) + +(define-base16-color-theme "gruvbox-dark-hard" + :base00 "#1d2021" + :base01 "#3c3836" + :base02 "#504945" + :base03 "#665c54" + :base04 "#bdae93" + :base05 "#d5c4a1" + :base06 "#ebdbb2" + :base07 "#fbf1c7" + :base08 "#fb4934" + :base09 "#fe8019" + :base0A "#fabd2f" + :base0B "#b8bb26" + :base0C "#8ec07c" + :base0D "#83a598" + :base0E "#d3869b" + :base0F "#d65d0e") + +;; Scheme: gruvbox-dark-medium +;; Scheme author: Dawid Kurek (dawikur@gmail.com), morhetz (https://github.com/morhetz/gruvbox) + +(define-base16-color-theme "gruvbox-dark-medium" + :base00 "#282828" + :base01 "#3c3836" + :base02 "#504945" + :base03 "#665c54" + :base04 "#bdae93" + :base05 "#d5c4a1" + :base06 "#ebdbb2" + :base07 "#fbf1c7" + :base08 "#fb4934" + :base09 "#fe8019" + :base0A "#fabd2f" + :base0B "#b8bb26" + :base0C "#8ec07c" + :base0D "#83a598" + :base0E "#d3869b" + :base0F "#d65d0e") + +;; Scheme: gruvbox-dark-pale +;; Scheme author: Dawid Kurek (dawikur@gmail.com), morhetz (https://github.com/morhetz/gruvbox) + +(define-base16-color-theme "gruvbox-dark-pale" + :base00 "#262626" + :base01 "#3a3a3a" + :base02 "#4e4e4e" + :base03 "#8a8a8a" + :base04 "#949494" + :base05 "#dab997" + :base06 "#d5c4a1" + :base07 "#ebdbb2" + :base08 "#d75f5f" + :base09 "#ff8700" + :base0A "#ffaf00" + :base0B "#afaf00" + :base0C "#85ad85" + :base0D "#83adad" + :base0E "#d485ad" + :base0F "#d65d0e") + +;; Scheme: gruvbox-dark-soft +;; Scheme author: Dawid Kurek (dawikur@gmail.com), morhetz (https://github.com/morhetz/gruvbox) + +(define-base16-color-theme "gruvbox-dark-soft" + :base00 "#32302f" + :base01 "#3c3836" + :base02 "#504945" + :base03 "#665c54" + :base04 "#bdae93" + :base05 "#d5c4a1" + :base06 "#ebdbb2" + :base07 "#fbf1c7" + :base08 "#fb4934" + :base09 "#fe8019" + :base0A "#fabd2f" + :base0B "#b8bb26" + :base0C "#8ec07c" + :base0D "#83a598" + :base0E "#d3869b" + :base0F "#d65d0e") + +;; Scheme: gruvbox-light-hard +;; Scheme author: Dawid Kurek (dawikur@gmail.com), morhetz (https://github.com/morhetz/gruvbox) + +(define-base16-color-theme "gruvbox-light-hard" + :base00 "#f9f5d7" + :base01 "#ebdbb2" + :base02 "#d5c4a1" + :base03 "#bdae93" + :base04 "#665c54" + :base05 "#504945" + :base06 "#3c3836" + :base07 "#282828" + :base08 "#9d0006" + :base09 "#af3a03" + :base0A "#b57614" + :base0B "#79740e" + :base0C "#427b58" + :base0D "#076678" + :base0E "#8f3f71" + :base0F "#d65d0e") + +;; Scheme: gruvbox-light-medium +;; Scheme author: Dawid Kurek (dawikur@gmail.com), morhetz (https://github.com/morhetz/gruvbox) + +(define-base16-color-theme "gruvbox-light-medium" + :base00 "#fbf1c7" + :base01 "#ebdbb2" + :base02 "#d5c4a1" + :base03 "#bdae93" + :base04 "#665c54" + :base05 "#504945" + :base06 "#3c3836" + :base07 "#282828" + :base08 "#9d0006" + :base09 "#af3a03" + :base0A "#b57614" + :base0B "#79740e" + :base0C "#427b58" + :base0D "#076678" + :base0E "#8f3f71" + :base0F "#d65d0e") + +;; Scheme: gruvbox-light-soft +;; Scheme author: Dawid Kurek (dawikur@gmail.com), morhetz (https://github.com/morhetz/gruvbox) + +(define-base16-color-theme "gruvbox-light-soft" + :base00 "#f2e5bc" + :base01 "#ebdbb2" + :base02 "#d5c4a1" + :base03 "#bdae93" + :base04 "#665c54" + :base05 "#504945" + :base06 "#3c3836" + :base07 "#282828" + :base08 "#9d0006" + :base09 "#af3a03" + :base0A "#b57614" + :base0B "#79740e" + :base0C "#427b58" + :base0D "#076678" + :base0E "#8f3f71" + :base0F "#d65d0e") + +;; Scheme: hardcore +;; Scheme author: Chris Caller + +(define-base16-color-theme "hardcore" + :base00 "#212121" + :base01 "#303030" + :base02 "#353535" + :base03 "#4A4A4A" + :base04 "#707070" + :base05 "#cdcdcd" + :base06 "#e5e5e5" + :base07 "#ffffff" + :base08 "#f92672" + :base09 "#fd971f" + :base0A "#e6db74" + :base0B "#a6e22e" + :base0C "#708387" + :base0D "#66d9ef" + :base0E "#9e6ffe" + :base0F "#e8b882") + +;; Scheme: heetch-light +;; Scheme author: Geoffrey Teale (tealeg@gmail.com) + +(define-base16-color-theme "heetch-light" + :base00 "#feffff" + :base01 "#392551" + :base02 "#7b6d8b" + :base03 "#9c92a8" + :base04 "#ddd6e5" + :base05 "#5a496e" + :base06 "#470546" + :base07 "#190134" + :base08 "#27d9d5" + :base09 "#bdb6c5" + :base0A "#5ba2b6" + :base0B "#f80059" + :base0C "#c33678" + :base0D "#47f9f5" + :base0E "#bd0152" + :base0F "#dedae2") + +;; Scheme: heetch +;; Scheme author: Geoffrey Teale (tealeg@gmail.com) + +(define-base16-color-theme "heetch" + :base00 "#190134" + :base01 "#392551" + :base02 "#5A496E" + :base03 "#7B6D8B" + :base04 "#9C92A8" + :base05 "#BDB6C5" + :base06 "#DEDAE2" + :base07 "#FEFFFF" + :base08 "#27D9D5" + :base09 "#5BA2B6" + :base0A "#8F6C97" + :base0B "#C33678" + :base0C "#F80059" + :base0D "#BD0152" + :base0E "#82034C" + :base0F "#470546") + +;; Scheme: helios +;; Scheme author: Alex Meyer (https://github.com/reyemxela) + +(define-base16-color-theme "helios" + :base00 "#1d2021" + :base01 "#383c3e" + :base02 "#53585b" + :base03 "#6f7579" + :base04 "#cdcdcd" + :base05 "#d5d5d5" + :base06 "#dddddd" + :base07 "#e5e5e5" + :base08 "#d72638" + :base09 "#eb8413" + :base0A "#f19d1a" + :base0B "#88b92d" + :base0C "#1ba595" + :base0D "#1e8bac" + :base0E "#be4264" + :base0F "#c85e0d") + +;; Scheme: horizon-dark +;; Scheme author: Michaël Ball (http://github.com/michael-ball/) + +(define-base16-color-theme "horizon-dark" + :base00 "#1C1E26" + :base01 "#232530" + :base02 "#2E303E" + :base03 "#6F6F70" + :base04 "#9DA0A2" + :base05 "#CBCED0" + :base06 "#DCDFE4" + :base07 "#E3E6EE" + :base08 "#E93C58" + :base09 "#E58D7D" + :base0A "#EFB993" + :base0B "#EFAF8E" + :base0C "#24A8B4" + :base0D "#DF5273" + :base0E "#B072D1" + :base0F "#E4A382") + +;; Scheme: horizon-light +;; Scheme author: Michaël Ball (http://github.com/michael-ball/) + +(define-base16-color-theme "horizon-light" + :base00 "#FDF0ED" + :base01 "#FADAD1" + :base02 "#F9CBBE" + :base03 "#BDB3B1" + :base04 "#948C8A" + :base05 "#403C3D" + :base06 "#302C2D" + :base07 "#201C1D" + :base08 "#F7939B" + :base09 "#F6661E" + :base0A "#FBE0D9" + :base0B "#94E1B0" + :base0C "#DC3318" + :base0D "#DA103F" + :base0E "#1D8991" + :base0F "#E58C92") + +;; Scheme: horizon-terminal-dark +;; Scheme author: Michaël Ball (http://github.com/michael-ball/) + +(define-base16-color-theme "horizon-terminal-dark" + :base00 "#1C1E26" + :base01 "#232530" + :base02 "#2E303E" + :base03 "#6F6F70" + :base04 "#9DA0A2" + :base05 "#CBCED0" + :base06 "#DCDFE4" + :base07 "#E3E6EE" + :base08 "#E95678" + :base09 "#FAB795" + :base0A "#FAC29A" + :base0B "#29D398" + :base0C "#59E1E3" + :base0D "#26BBD9" + :base0E "#EE64AC" + :base0F "#F09383") + +;; Scheme: horizon-terminal-light +;; Scheme author: Michaël Ball (http://github.com/michael-ball/) + +(define-base16-color-theme "horizon-terminal-light" + :base00 "#FDF0ED" + :base01 "#FADAD1" + :base02 "#F9CBBE" + :base03 "#BDB3B1" + :base04 "#948C8A" + :base05 "#403C3D" + :base06 "#302C2D" + :base07 "#201C1D" + :base08 "#E95678" + :base09 "#F9CEC3" + :base0A "#FADAD1" + :base0B "#29D398" + :base0C "#59E1E3" + :base0D "#26BBD9" + :base0E "#EE64AC" + :base0F "#F9CBBE") + +;; Scheme: humanoid-dark +;; Scheme author: Thomas (tasmo) Friese + +(define-base16-color-theme "humanoid-dark" + :base00 "#232629" + :base01 "#333b3d" + :base02 "#484e54" + :base03 "#60615d" + :base04 "#c0c0bd" + :base05 "#f8f8f2" + :base06 "#fcfcf6" + :base07 "#fcfcfc" + :base08 "#f11235" + :base09 "#ff9505" + :base0A "#ffb627" + :base0B "#02d849" + :base0C "#0dd9d6" + :base0D "#00a6fb" + :base0E "#f15ee3" + :base0F "#b27701") + +;; Scheme: humanoid-light +;; Scheme author: Thomas (tasmo) Friese + +(define-base16-color-theme "humanoid-light" + :base00 "#f8f8f2" + :base01 "#efefe9" + :base02 "#deded8" + :base03 "#c0c0bd" + :base04 "#60615d" + :base05 "#232629" + :base06 "#2f3337" + :base07 "#070708" + :base08 "#b0151a" + :base09 "#ff3d00" + :base0A "#ffb627" + :base0B "#388e3c" + :base0C "#008e8e" + :base0D "#0082c9" + :base0E "#700f98" + :base0F "#b27701") + +;; Scheme: ia-dark +;; Scheme author: iA Inc. (modified by aramisgithub) + +(define-base16-color-theme "ia-dark" + :base00 "#1a1a1a" + :base01 "#222222" + :base02 "#1d414d" + :base03 "#767676" + :base04 "#b8b8b8" + :base05 "#cccccc" + :base06 "#e8e8e8" + :base07 "#f8f8f8" + :base08 "#d88568" + :base09 "#d86868" + :base0A "#b99353" + :base0B "#83a471" + :base0C "#7c9cae" + :base0D "#8eccdd" + :base0E "#b98eb2" + :base0F "#8b6c37") + +;; Scheme: ia-light +;; Scheme author: iA Inc. (modified by aramisgithub) + +(define-base16-color-theme "ia-light" + :base00 "#f6f6f6" + :base01 "#dedede" + :base02 "#bde5f2" + :base03 "#898989" + :base04 "#767676" + :base05 "#181818" + :base06 "#e8e8e8" + :base07 "#f8f8f8" + :base08 "#9c5a02" + :base09 "#c43e18" + :base0A "#c48218" + :base0B "#38781c" + :base0C "#2d6bb1" + :base0D "#48bac2" + :base0E "#a94598" + :base0F "#8b6c37") + +;; Scheme: icy +;; Scheme author: icyphox (https://icyphox.ga) + +(define-base16-color-theme "icy" + :base00 "#021012" + :base01 "#031619" + :base02 "#041f23" + :base03 "#052e34" + :base04 "#064048" + :base05 "#095b67" + :base06 "#0c7c8c" + :base07 "#109cb0" + :base08 "#16c1d9" + :base09 "#b3ebf2" + :base0A "#80deea" + :base0B "#4dd0e1" + :base0C "#26c6da" + :base0D "#00bcd4" + :base0E "#00acc1" + :base0F "#0097a7") + +;; Scheme: kimber +;; Scheme author: Mishka Nguyen (https://github.com/akhsiM) + +(define-base16-color-theme "kimber" + :base00 "#222222" + :base01 "#313131" + :base02 "#555D55" + :base03 "#644646" + :base04 "#5A5A5A" + :base05 "#DEDEE7" + :base06 "#C3C3B4" + :base07 "#FFFFE6" + :base08 "#C88C8C" + :base09 "#476C88" + :base0A "#D8B56D" + :base0B "#99C899" + :base0C "#78B4B4" + :base0D "#537C9C" + :base0E "#86CACD" + :base0F "#704F4F") + +;; Scheme: materia +;; Scheme author: Defman21 + +(define-base16-color-theme "materia" + :base00 "#263238" + :base01 "#2C393F" + :base02 "#37474F" + :base03 "#707880" + :base04 "#C9CCD3" + :base05 "#CDD3DE" + :base06 "#D5DBE5" + :base07 "#FFFFFF" + :base08 "#EC5F67" + :base09 "#EA9560" + :base0A "#FFCC00" + :base0B "#8BD649" + :base0C "#80CBC4" + :base0D "#89DDFF" + :base0E "#82AAFF" + :base0F "#EC5F67") + +;; Scheme: material-vivid +;; Scheme author: joshyrobot + +(define-base16-color-theme "material-vivid" + :base00 "#202124" + :base01 "#27292c" + :base02 "#323639" + :base03 "#44464d" + :base04 "#676c71" + :base05 "#80868b" + :base06 "#9e9e9e" + :base07 "#ffffff" + :base08 "#f44336" + :base09 "#ff9800" + :base0A "#ffeb3b" + :base0B "#00e676" + :base0C "#00bcd4" + :base0D "#2196f3" + :base0E "#673ab7" + :base0F "#8d6e63") + +;; Scheme: material-darker +;; Scheme author: Nate Peterson + +(define-base16-color-theme "material-darker" + :base00 "#212121" + :base01 "#303030" + :base02 "#353535" + :base03 "#4A4A4A" + :base04 "#B2CCD6" + :base05 "#EEFFFF" + :base06 "#EEFFFF" + :base07 "#FFFFFF" + :base08 "#F07178" + :base09 "#F78C6C" + :base0A "#FFCB6B" + :base0B "#C3E88D" + :base0C "#89DDFF" + :base0D "#82AAFF" + :base0E "#C792EA" + :base0F "#FF5370") + +;; Scheme: material-lighter +;; Scheme author: Nate Peterson + +(define-base16-color-theme "material-lighter" + :base00 "#FAFAFA" + :base01 "#E7EAEC" + :base02 "#CCEAE7" + :base03 "#CCD7DA" + :base04 "#8796B0" + :base05 "#80CBC4" + :base06 "#80CBC4" + :base07 "#FFFFFF" + :base08 "#FF5370" + :base09 "#F76D47" + :base0A "#FFB62C" + :base0B "#91B859" + :base0C "#39ADB5" + :base0D "#6182B8" + :base0E "#7C4DFF" + :base0F "#E53935") + +;; Scheme: material-palenight +;; Scheme author: Nate Peterson + +(define-base16-color-theme "material-palenight" + :base00 "#292D3E" + :base01 "#444267" + :base02 "#32374D" + :base03 "#676E95" + :base04 "#8796B0" + :base05 "#959DCB" + :base06 "#959DCB" + :base07 "#FFFFFF" + :base08 "#F07178" + :base09 "#F78C6C" + :base0A "#FFCB6B" + :base0B "#C3E88D" + :base0C "#89DDFF" + :base0D "#82AAFF" + :base0E "#C792EA" + :base0F "#FF5370") + +;; Scheme: material +;; Scheme author: Nate Peterson + +(define-base16-color-theme "material" + :base00 "#263238" + :base01 "#2E3C43" + :base02 "#314549" + :base03 "#546E7A" + :base04 "#B2CCD6" + :base05 "#EEFFFF" + :base06 "#EEFFFF" + :base07 "#FFFFFF" + :base08 "#F07178" + :base09 "#F78C6C" + :base0A "#FFCB6B" + :base0B "#C3E88D" + :base0C "#89DDFF" + :base0D "#82AAFF" + :base0E "#C792EA" + :base0F "#FF5370") + +;; Scheme: mellow-purple +;; Scheme author: gidsi + +(define-base16-color-theme "mellow-purple" + :base00 "#1e0528" + :base01 "#1A092D" + :base02 "#331354" + :base03 "#320f55" + :base04 "#873582" + :base05 "#ffeeff" + :base06 "#ffeeff" + :base07 "#f8c0ff" + :base08 "#00d9e9" + :base09 "#aa00a3" + :base0A "#955ae7" + :base0B "#05cb0d" + :base0C "#b900b1" + :base0D "#550068" + :base0E "#8991bb" + :base0F "#4d6fff") + +;; Scheme: mexico-light +;; Scheme author: Sheldon Johnson + +(define-base16-color-theme "mexico-light" + :base00 "#f8f8f8" + :base01 "#e8e8e8" + :base02 "#d8d8d8" + :base03 "#b8b8b8" + :base04 "#585858" + :base05 "#383838" + :base06 "#282828" + :base07 "#181818" + :base08 "#ab4642" + :base09 "#dc9656" + :base0A "#f79a0e" + :base0B "#538947" + :base0C "#4b8093" + :base0D "#7cafc2" + :base0E "#96609e" + :base0F "#a16946") + +;; Scheme: nebula +;; Scheme author: Gabriel Fontes (https://github.com/Misterio77) + +(define-base16-color-theme "nebula" + :base00 "#22273b" + :base01 "#414f60" + :base02 "#5a8380" + :base03 "#6e6f72" + :base04 "#87888b" + :base05 "#a4a6a9" + :base06 "#c7c9cd" + :base07 "#8dbdaa" + :base08 "#777abc" + :base09 "#94929e" + :base0A "#4f9062" + :base0B "#6562a8" + :base0C "#226f68" + :base0D "#4d6bb6" + :base0E "#716cae" + :base0F "#8c70a7") + +;; Scheme: nord +;; Scheme author: arcticicestudio + +(define-base16-color-theme "nord" + :base00 "#2E3440" + :base01 "#3B4252" + :base02 "#434C5E" + :base03 "#4C566A" + :base04 "#D8DEE9" + :base05 "#E5E9F0" + :base06 "#ECEFF4" + :base07 "#8FBCBB" + :base08 "#BF616A" + :base09 "#D08770" + :base0A "#EBCB8B" + :base0B "#A3BE8C" + :base0C "#88C0D0" + :base0D "#81A1C1" + :base0E "#B48EAD" + :base0F "#5E81AC") + +;; Scheme: nova +;; Scheme author: George Essig (https://github.com/gessig), Trevor D. Miller (https://trevordmiller.com) + +(define-base16-color-theme "nova" + :base00 "#3C4C55" + :base01 "#556873" + :base02 "#6A7D89" + :base03 "#899BA6" + :base04 "#899BA6" + :base05 "#C5D4DD" + :base06 "#899BA6" + :base07 "#556873" + :base08 "#83AFE5" + :base09 "#7FC1CA" + :base0A "#A8CE93" + :base0B "#7FC1CA" + :base0C "#F2C38F" + :base0D "#83AFE5" + :base0E "#9A93E1" + :base0F "#F2C38F") + +;; Scheme: one-light +;; Scheme author: Daniel Pfeifer (http://github.com/purpleKarrot) + +(define-base16-color-theme "one-light" + :base00 "#fafafa" + :base01 "#f0f0f1" + :base02 "#e5e5e6" + :base03 "#a0a1a7" + :base04 "#696c77" + :base05 "#383a42" + :base06 "#202227" + :base07 "#090a0b" + :base08 "#ca1243" + :base09 "#d75f00" + :base0A "#c18401" + :base0B "#50a14f" + :base0C "#0184bc" + :base0D "#4078f2" + :base0E "#a626a4" + :base0F "#986801") + +;; Scheme: onedark +;; Scheme author: Lalit Magant (http://github.com/tilal6991) + +(define-base16-color-theme "onedark" + :base00 "#282c34" + :base01 "#353b45" + :base02 "#3e4451" + :base03 "#545862" + :base04 "#565c64" + :base05 "#abb2bf" + :base06 "#b6bdca" + :base07 "#c8ccd4" + :base08 "#e06c75" + :base09 "#d19a66" + :base0A "#e5c07b" + :base0B "#98c379" + :base0C "#56b6c2" + :base0D "#61afef" + :base0E "#c678dd" + :base0F "#be5046") + +;; Scheme: outrun-dark +;; Scheme author: Hugo Delahousse (http://github.com/hugodelahousse/) + +(define-base16-color-theme "outrun-dark" + :base00 "#00002A" + :base01 "#20204A" + :base02 "#30305A" + :base03 "#50507A" + :base04 "#B0B0DA" + :base05 "#D0D0FA" + :base06 "#E0E0FF" + :base07 "#F5F5FF" + :base08 "#FF4242" + :base09 "#FC8D28" + :base0A "#F3E877" + :base0B "#59F176" + :base0C "#0EF0F0" + :base0D "#66B0FF" + :base0E "#F10596" + :base0F "#F003EF") + +;; Scheme: papercolor-dark +;; Scheme author: Jon Leopard (http://github.com/jonleopard) based on PaperColor Theme (https://github.com/NLKNguyen/papercolor-theme) + +(define-base16-color-theme "papercolor-dark" + :base00 "#1c1c1c" + :base01 "#af005f" + :base02 "#5faf00" + :base03 "#d7af5f" + :base04 "#5fafd7" + :base05 "#808080" + :base06 "#d7875f" + :base07 "#d0d0d0" + :base08 "#585858" + :base09 "#5faf5f" + :base0A "#afd700" + :base0B "#af87d7" + :base0C "#ffaf00" + :base0D "#ff5faf" + :base0E "#00afaf" + :base0F "#5f8787") + +;; Scheme: papercolor-light +;; Scheme author: Jon Leopard (http://github.com/jonleopard) based on PaperColor Theme (https://github.com/NLKNguyen/papercolor-theme) + +(define-base16-color-theme "papercolor-light" + :base00 "#eeeeee" + :base01 "#af0000" + :base02 "#008700" + :base03 "#5f8700" + :base04 "#0087af" + :base05 "#444444" + :base06 "#005f87" + :base07 "#878787" + :base08 "#bcbcbc" + :base09 "#d70000" + :base0A "#d70087" + :base0B "#8700af" + :base0C "#d75f00" + :base0D "#d75f00" + :base0E "#005faf" + :base0F "#005f87") + +;; Scheme: pasque +;; Scheme author: Gabriel Fontes (https://github.com/Misterio77) + +(define-base16-color-theme "pasque" + :base00 "#271C3A" + :base01 "#100323" + :base02 "#3E2D5C" + :base03 "#5D5766" + :base04 "#BEBCBF" + :base05 "#DEDCDF" + :base06 "#EDEAEF" + :base07 "#BBAADD" + :base08 "#A92258" + :base09 "#918889" + :base0A "#804ead" + :base0B "#C6914B" + :base0C "#7263AA" + :base0D "#8E7DC6" + :base0E "#953B9D" + :base0F "#59325C") + +;; Scheme: pinky +;; Scheme author: Benjamin (https://github.com/b3nj5m1n) + +(define-base16-color-theme "pinky" + :base00 "#171517" + :base01 "#1b181b" + :base02 "#1d1b1d" + :base03 "#383338" + :base04 "#e7dbdb" + :base05 "#f5f5f5" + :base06 "#ffffff" + :base07 "#f7f3f7" + :base08 "#ffa600" + :base09 "#00ff66" + :base0A "#20df6c" + :base0B "#ff0066" + :base0C "#6600ff" + :base0D "#00ffff" + :base0E "#007fff" + :base0F "#df206c") + +;; Scheme: porple +;; Scheme author: Niek den Breeje (https://github.com/AuditeMarlow) + +(define-base16-color-theme "porple" + :base00 "#292c36" + :base01 "#333344" + :base02 "#474160" + :base03 "#65568a" + :base04 "#b8b8b8" + :base05 "#d8d8d8" + :base06 "#e8e8e8" + :base07 "#f8f8f8" + :base08 "#f84547" + :base09 "#d28e5d" + :base0A "#efa16b" + :base0B "#95c76f" + :base0C "#64878f" + :base0D "#8485ce" + :base0E "#b74989" + :base0F "#986841") + +;; Scheme: qualia +;; Scheme author: isaacwhanson + +(define-base16-color-theme "qualia" + :base00 "#101010" + :base01 "#454545" + :base02 "#454545" + :base03 "#454545" + :base04 "#808080" + :base05 "#C0C0C0" + :base06 "#C0C0C0" + :base07 "#454545" + :base08 "#EFA6A2" + :base09 "#A3B8EF" + :base0A "#E6A3DC" + :base0B "#80C990" + :base0C "#C8C874" + :base0D "#50CACD" + :base0E "#E0AF85" + :base0F "#808080") + +;; Scheme: rebecca +;; Scheme author: Victor Borja (http://github.com/vic) based on Rebecca Theme (http://github.com/vic/rebecca-theme) + +(define-base16-color-theme "rebecca" + :base00 "#292a44" + :base01 "#663399" + :base02 "#383a62" + :base03 "#666699" + :base04 "#a0a0c5" + :base05 "#f1eff8" + :base06 "#ccccff" + :base07 "#53495d" + :base08 "#a0a0c5" + :base09 "#efe4a1" + :base0A "#ae81ff" + :base0B "#6dfedf" + :base0C "#8eaee0" + :base0D "#2de0a7" + :base0E "#7aa5ff" + :base0F "#ff79c6") + +;; Scheme: rose-pine-dawn +;; Scheme author: Emilia Dunfelt <sayhi@dunfelt.se> + +(define-base16-color-theme "rose-pine-dawn" + :base00 "#faf4ed" + :base01 "#fffaf3" + :base02 "#f2e9de" + :base03 "#9893a5" + :base04 "#6e6a86" + :base05 "#575279" + :base06 "#555169" + :base07 "#26233a" + :base08 "#1f1d2e" + :base09 "#b4637a" + :base0A "#ea9d34" + :base0B "#d7827e" + :base0C "#286983" + :base0D "#56949f" + :base0E "#907aa9" + :base0F "#c5c3ce") + +;; Scheme: rose-pine-moon +;; Scheme author: Emilia Dunfelt <sayhi@dunfelt.se> + +(define-base16-color-theme "rose-pine-moon" + :base00 "#232136" + :base01 "#2a273f" + :base02 "#393552" + :base03 "#59546d" + :base04 "#817c9c" + :base05 "#e0def4" + :base06 "#f5f5f7" + :base07 "#d9d7e1" + :base08 "#ecebf0" + :base09 "#eb6f92" + :base0A "#f6c177" + :base0B "#ea9a97" + :base0C "#3e8fb0" + :base0D "#9ccfd8" + :base0E "#c4a7e7" + :base0F "#b9b9bc") + +;; Scheme: rose-pine +;; Scheme author: Emilia Dunfelt <sayhi@dunfelt.se> + +(define-base16-color-theme "rose-pine" + :base00 "#191724" + :base01 "#1f1d2e" + :base02 "#26233a" + :base03 "#555169" + :base04 "#6e6a86" + :base05 "#e0def4" + :base06 "#f0f0f3" + :base07 "#c5c3ce" + :base08 "#e2e1e7" + :base09 "#eb6f92" + :base0A "#f6c177" + :base0B "#ebbcba" + :base0C "#31748f" + :base0D "#9ccfd8" + :base0E "#c4a7e7" + :base0F "#e5e5e5") + +;; Scheme: sagelight +;; Scheme author: Carter Veldhuizen + +(define-base16-color-theme "sagelight" + :base00 "#f8f8f8" + :base01 "#e8e8e8" + :base02 "#d8d8d8" + :base03 "#b8b8b8" + :base04 "#585858" + :base05 "#383838" + :base06 "#282828" + :base07 "#181818" + :base08 "#fa8480" + :base09 "#ffaa61" + :base0A "#ffdc61" + :base0B "#a0d2c8" + :base0C "#a2d6f5" + :base0D "#a0a7d2" + :base0E "#c8a0d2" + :base0F "#d2b2a0") + +;; Scheme: sakura +;; Scheme author: Misterio77 (http://github.com/Misterio77) + +(define-base16-color-theme "sakura" + :base00 "#feedf3" + :base01 "#f8e2e7" + :base02 "#e0ccd1" + :base03 "#755f64" + :base04 "#665055" + :base05 "#564448" + :base06 "#42383a" + :base07 "#33292b" + :base08 "#df2d52" + :base09 "#f6661e" + :base0A "#c29461" + :base0B "#2e916d" + :base0C "#1d8991" + :base0D "#006e93" + :base0E "#5e2180" + :base0F "#ba0d35") + +;; Scheme: sandcastle +;; Scheme author: George Essig (https://github.com/gessig) + +(define-base16-color-theme "sandcastle" + :base00 "#282c34" + :base01 "#2c323b" + :base02 "#3e4451" + :base03 "#665c54" + :base04 "#928374" + :base05 "#a89984" + :base06 "#d5c4a1" + :base07 "#fdf4c1" + :base08 "#83a598" + :base09 "#a07e3b" + :base0A "#a07e3b" + :base0B "#528b8b" + :base0C "#83a598" + :base0D "#83a598" + :base0E "#d75f5f" + :base0F "#a87322") + +;; Scheme: shades-of-purple +;; Scheme author: Iolar Demartini Junior (http://github.com/demartini) based on Shades of Purple Theme (https://github.com/ahmadawais/shades-of-purple-vscode). + +(define-base16-color-theme "shades-of-purple" + :base00 "#1e1e3f" + :base01 "#43d426" + :base02 "#f1d000" + :base03 "#808080" + :base04 "#6871ff" + :base05 "#c7c7c7" + :base06 "#ff77ff" + :base07 "#ffffff" + :base08 "#d90429" + :base09 "#f92a1c" + :base0A "#ffe700" + :base0B "#3ad900" + :base0C "#00c5c7" + :base0D "#6943ff" + :base0E "#ff2c70" + :base0F "#79e8fb") + +;; Scheme: silk-dark +;; Scheme author: Gabriel Fontes (https://github.com/Misterio77) + +(define-base16-color-theme "silk-dark" + :base00 "#0e3c46" + :base01 "#1D494E" + :base02 "#2A5054" + :base03 "#587073" + :base04 "#9DC8CD" + :base05 "#C7DBDD" + :base06 "#CBF2F7" + :base07 "#D2FAFF" + :base08 "#fb6953" + :base09 "#fcab74" + :base0A "#fce380" + :base0B "#73d8ad" + :base0C "#3fb2b9" + :base0D "#46bddd" + :base0E "#756b8a" + :base0F "#9b647b") + +;; Scheme: silk-light +;; Scheme author: Gabriel Fontes (https://github.com/Misterio77) + +(define-base16-color-theme "silk-light" + :base00 "#E9F1EF" + :base01 "#CCD4D3" + :base02 "#90B7B6" + :base03 "#5C787B" + :base04 "#4B5B5F" + :base05 "#385156" + :base06 "#0e3c46" + :base07 "#D2FAFF" + :base08 "#CF432E" + :base09 "#D27F46" + :base0A "#CFAD25" + :base0B "#6CA38C" + :base0C "#329CA2" + :base0D "#39AAC9" + :base0E "#6E6582" + :base0F "#865369") + +;; Scheme: snazzy +;; Scheme author: Chawye Hsu (https://github.com/chawyehsu) based on Hyper Snazzy Theme (https://github.com/sindresorhus/hyper-snazzy) + +(define-base16-color-theme "snazzy" + :base00 "#282a36" + :base01 "#34353e" + :base02 "#43454f" + :base03 "#78787e" + :base04 "#a5a5a9" + :base05 "#e2e4e5" + :base06 "#eff0eb" + :base07 "#f1f1f0" + :base08 "#ff5c57" + :base09 "#ff9f43" + :base0A "#f3f99d" + :base0B "#5af78e" + :base0C "#9aedfe" + :base0D "#57c7ff" + :base0E "#ff6ac1" + :base0F "#b2643c") + +;; Scheme: solarflare-light +;; Scheme author: Chuck Harmston (https://chuck.harmston.ch) + +(define-base16-color-theme "solarflare-light" + :base00 "#F5F7FA" + :base01 "#E8E9ED" + :base02 "#A6AFB8" + :base03 "#85939E" + :base04 "#667581" + :base05 "#586875" + :base06 "#222E38" + :base07 "#18262F" + :base08 "#EF5253" + :base09 "#E66B2B" + :base0A "#E4B51C" + :base0B "#7CC844" + :base0C "#52CBB0" + :base0D "#33B5E1" + :base0E "#A363D5" + :base0F "#D73C9A") + +;; Scheme: solarflare +;; Scheme author: Chuck Harmston (https://chuck.harmston.ch) + +(define-base16-color-theme "solarflare" + :base00 "#18262F" + :base01 "#222E38" + :base02 "#586875" + :base03 "#667581" + :base04 "#85939E" + :base05 "#A6AFB8" + :base06 "#E8E9ED" + :base07 "#F5F7FA" + :base08 "#EF5253" + :base09 "#E66B2B" + :base0A "#E4B51C" + :base0B "#7CC844" + :base0C "#52CBB0" + :base0D "#33B5E1" + :base0E "#A363D5" + :base0F "#D73C9A") + +;; Scheme: solarized-dark +;; Scheme author: Ethan Schoonover (modified by aramisgithub) + +(define-base16-color-theme "solarized-dark" + :base00 "#002b36" + :base01 "#073642" + :base02 "#586e75" + :base03 "#657b83" + :base04 "#839496" + :base05 "#93a1a1" + :base06 "#eee8d5" + :base07 "#fdf6e3" + :base08 "#dc322f" + :base09 "#cb4b16" + :base0A "#b58900" + :base0B "#859900" + :base0C "#2aa198" + :base0D "#268bd2" + :base0E "#6c71c4" + :base0F "#d33682") + +;; Scheme: solarized-light +;; Scheme author: Ethan Schoonover (modified by aramisgithub) + +(define-base16-color-theme "solarized-light" + :base00 "#fdf6e3" + :base01 "#eee8d5" + :base02 "#93a1a1" + :base03 "#839496" + :base04 "#657b83" + :base05 "#586e75" + :base06 "#073642" + :base07 "#002b36" + :base08 "#dc322f" + :base09 "#cb4b16" + :base0A "#b58900" + :base0B "#859900" + :base0C "#2aa198" + :base0D "#268bd2" + :base0E "#6c71c4" + :base0F "#d33682") + +;; Scheme: summercamp +;; Scheme author: zoe firi (zoefiri.github.io) + +(define-base16-color-theme "summercamp" + :base00 "#1c1810" + :base01 "#2a261c" + :base02 "#3a3527" + :base03 "#504b38" + :base04 "#5f5b45" + :base05 "#736e55" + :base06 "#bab696" + :base07 "#f8f5de" + :base08 "#e35142" + :base09 "#fba11b" + :base0A "#f2ff27" + :base0B "#5ceb5a" + :base0C "#5aebbc" + :base0D "#489bf0" + :base0E "#FF8080" + :base0F "#F69BE7") + +;; Scheme: summerfruit-dark +;; Scheme author: Christopher Corley (http://christop.club/) + +(define-base16-color-theme "summerfruit-dark" + :base00 "#151515" + :base01 "#202020" + :base02 "#303030" + :base03 "#505050" + :base04 "#B0B0B0" + :base05 "#D0D0D0" + :base06 "#E0E0E0" + :base07 "#FFFFFF" + :base08 "#FF0086" + :base09 "#FD8900" + :base0A "#ABA800" + :base0B "#00C918" + :base0C "#1FAAAA" + :base0D "#3777E6" + :base0E "#AD00A1" + :base0F "#CC6633") + +;; Scheme: summerfruit-light +;; Scheme author: Christopher Corley (http://christop.club/) + +(define-base16-color-theme "summerfruit-light" + :base00 "#FFFFFF" + :base01 "#E0E0E0" + :base02 "#D0D0D0" + :base03 "#B0B0B0" + :base04 "#000000" + :base05 "#101010" + :base06 "#151515" + :base07 "#202020" + :base08 "#FF0086" + :base09 "#FD8900" + :base0A "#ABA800" + :base0B "#00C918" + :base0C "#1FAAAA" + :base0D "#3777E6" + :base0E "#AD00A1" + :base0F "#CC6633") + +;; Scheme: synth-midnight-dark +;; Scheme author: Michaël Ball (http://github.com/michael-ball/) + +(define-base16-color-theme "synth-midnight-dark" + :base00 "#050608" + :base01 "#1a1b1c" + :base02 "#28292a" + :base03 "#474849" + :base04 "#a3a5a6" + :base05 "#c1c3c4" + :base06 "#cfd1d2" + :base07 "#dddfe0" + :base08 "#b53b50" + :base09 "#ea770d" + :base0A "#c9d364" + :base0B "#06ea61" + :base0C "#42fff9" + :base0D "#03aeff" + :base0E "#ea5ce2" + :base0F "#cd6320") + +;; Scheme: synth-midnight-light +;; Scheme author: Michaël Ball (http://github.com/michael-ball/) + +(define-base16-color-theme "synth-midnight-light" + :base00 "#dddfe0" + :base01 "#cfd1d2" + :base02 "#c1c3c4" + :base03 "#a3a5a6" + :base04 "#474849" + :base05 "#28292a" + :base06 "#1a1b1c" + :base07 "#050608" + :base08 "#b53b50" + :base09 "#ea770d" + :base0A "#c9d364" + :base0B "#06ea61" + :base0C "#42fff9" + :base0D "#03aeff" + :base0E "#ea5ce2" + :base0F "#cd6320") + +;; Scheme: tango +;; Scheme author: @Schnouki, based on the Tango Desktop Project + +(define-base16-color-theme "tango" + :base00 "#2e3436" + :base01 "#8ae234" + :base02 "#fce94f" + :base03 "#555753" + :base04 "#729fcf" + :base05 "#d3d7cf" + :base06 "#ad7fa8" + :base07 "#eeeeec" + :base08 "#cc0000" + :base09 "#ef2929" + :base0A "#c4a000" + :base0B "#4e9a06" + :base0C "#06989a" + :base0D "#3465a4" + :base0E "#75507b" + :base0F "#34e2e2") + +;; Scheme: tender +;; Scheme author: Jacobo Tabernero (https://github/com/jacoborus/tender.vim) + +(define-base16-color-theme "tender" + :base00 "#282828" + :base01 "#383838" + :base02 "#484848" + :base03 "#4c4c4c" + :base04 "#b8b8b8" + :base05 "#eeeeee" + :base06 "#e8e8e8" + :base07 "#feffff" + :base08 "#f43753" + :base09 "#dc9656" + :base0A "#ffc24b" + :base0B "#c9d05c" + :base0C "#73cef4" + :base0D "#b3deef" + :base0E "#d3b987" + :base0F "#a16946") + +;; Scheme: twilight +;; Scheme author: David Hart (https://github.com/hartbit) + +(define-base16-color-theme "twilight" + :base00 "#1e1e1e" + :base01 "#323537" + :base02 "#464b50" + :base03 "#5f5a60" + :base04 "#838184" + :base05 "#a7a7a7" + :base06 "#c3c3c3" + :base07 "#ffffff" + :base08 "#cf6a4c" + :base09 "#cda869" + :base0A "#f9ee98" + :base0B "#8f9d6a" + :base0C "#afc4db" + :base0D "#7587a6" + :base0E "#9b859d" + :base0F "#9b703f") + +;; Scheme: 3024 +;; Scheme author: Jan T. Sott (http://github.com/idleberg) + +(define-base16-color-theme "3024" + :base00 "#090300" + :base01 "#3a3432" + :base02 "#4a4543" + :base03 "#5c5855" + :base04 "#807d7c" + :base05 "#a5a2a2" + :base06 "#d6d5d4" + :base07 "#f7f7f7" + :base08 "#db2d20" + :base09 "#e8bbd0" + :base0A "#fded02" + :base0B "#01a252" + :base0C "#b5e4f4" + :base0D "#01a0e4" + :base0E "#a16a94" + :base0F "#cdab53") + +;; Scheme: apathy +;; Scheme author: Jannik Siebert (https://github.com/janniks) + +(define-base16-color-theme "apathy" + :base00 "#031A16" + :base01 "#0B342D" + :base02 "#184E45" + :base03 "#2B685E" + :base04 "#5F9C92" + :base05 "#81B5AC" + :base06 "#A7CEC8" + :base07 "#D2E7E4" + :base08 "#3E9688" + :base09 "#3E7996" + :base0A "#3E4C96" + :base0B "#883E96" + :base0C "#963E4C" + :base0D "#96883E" + :base0E "#4C963E" + :base0F "#3E965B") + +;; Scheme: ashes +;; Scheme author: Jannik Siebert (https://github.com/janniks) + +(define-base16-color-theme "ashes" + :base00 "#1C2023" + :base01 "#393F45" + :base02 "#565E65" + :base03 "#747C84" + :base04 "#ADB3BA" + :base05 "#C7CCD1" + :base06 "#DFE2E5" + :base07 "#F3F4F5" + :base08 "#C7AE95" + :base09 "#C7C795" + :base0A "#AEC795" + :base0B "#95C7AE" + :base0C "#95AEC7" + :base0D "#AE95C7" + :base0E "#C795AE" + :base0F "#C79595") + +;; Scheme: bespin +;; Scheme author: Jan T. Sott + +(define-base16-color-theme "bespin" + :base00 "#28211c" + :base01 "#36312e" + :base02 "#5e5d5c" + :base03 "#666666" + :base04 "#797977" + :base05 "#8a8986" + :base06 "#9d9b97" + :base07 "#baae9e" + :base08 "#cf6a4c" + :base09 "#cf7d34" + :base0A "#f9ee98" + :base0B "#54be0d" + :base0C "#afc4db" + :base0D "#5ea6ea" + :base0E "#9b859d" + :base0F "#937121") + +;; Scheme: brewer +;; Scheme author: Timothée Poisot (http://github.com/tpoisot) + +(define-base16-color-theme "brewer" + :base00 "#0c0d0e" + :base01 "#2e2f30" + :base02 "#515253" + :base03 "#737475" + :base04 "#959697" + :base05 "#b7b8b9" + :base06 "#dadbdc" + :base07 "#fcfdfe" + :base08 "#e31a1c" + :base09 "#e6550d" + :base0A "#dca060" + :base0B "#31a354" + :base0C "#80b1d3" + :base0D "#3182bd" + :base0E "#756bb1" + :base0F "#b15928") + +;; Scheme: bright +;; Scheme author: Chris Kempson (http://chriskempson.com) + +(define-base16-color-theme "bright" + :base00 "#000000" + :base01 "#303030" + :base02 "#505050" + :base03 "#b0b0b0" + :base04 "#d0d0d0" + :base05 "#e0e0e0" + :base06 "#f5f5f5" + :base07 "#ffffff" + :base08 "#fb0120" + :base09 "#fc6d24" + :base0A "#fda331" + :base0B "#a1c659" + :base0C "#76c7b7" + :base0D "#6fb3d2" + :base0E "#d381c3" + :base0F "#be643c") + +;; Scheme: chalk +;; Scheme author: Chris Kempson (http://chriskempson.com) + +(define-base16-color-theme "chalk" + :base00 "#151515" + :base01 "#202020" + :base02 "#303030" + :base03 "#505050" + :base04 "#b0b0b0" + :base05 "#d0d0d0" + :base06 "#e0e0e0" + :base07 "#f5f5f5" + :base08 "#fb9fb1" + :base09 "#eda987" + :base0A "#ddb26f" + :base0B "#acc267" + :base0C "#12cfc0" + :base0D "#6fc2ef" + :base0E "#e1a3ee" + :base0F "#deaf8f") + +;; Scheme: darktooth +;; Scheme author: Jason Milkins (https://github.com/jasonm23) + +(define-base16-color-theme "darktooth" + :base00 "#1D2021" + :base01 "#32302F" + :base02 "#504945" + :base03 "#665C54" + :base04 "#928374" + :base05 "#A89984" + :base06 "#D5C4A1" + :base07 "#FDF4C1" + :base08 "#FB543F" + :base09 "#FE8625" + :base0A "#FAC03B" + :base0B "#95C085" + :base0C "#8BA59B" + :base0D "#0D6678" + :base0E "#8F4673" + :base0F "#A87322") + +;; Scheme: embers +;; Scheme author: Jannik Siebert (https://github.com/janniks) + +(define-base16-color-theme "embers" + :base00 "#16130F" + :base01 "#2C2620" + :base02 "#433B32" + :base03 "#5A5047" + :base04 "#8A8075" + :base05 "#A39A90" + :base06 "#BEB6AE" + :base07 "#DBD6D1" + :base08 "#826D57" + :base09 "#828257" + :base0A "#6D8257" + :base0B "#57826D" + :base0C "#576D82" + :base0D "#6D5782" + :base0E "#82576D" + :base0F "#825757") + +;; Scheme: flat +;; Scheme author: Chris Kempson (http://chriskempson.com) + +(define-base16-color-theme "flat" + :base00 "#2C3E50" + :base01 "#34495E" + :base02 "#7F8C8D" + :base03 "#95A5A6" + :base04 "#BDC3C7" + :base05 "#e0e0e0" + :base06 "#f5f5f5" + :base07 "#ECF0F1" + :base08 "#E74C3C" + :base09 "#E67E22" + :base0A "#F1C40F" + :base0B "#2ECC71" + :base0C "#1ABC9C" + :base0D "#3498DB" + :base0E "#9B59B6" + :base0F "#be643c") + +;; Scheme: google-dark +;; Scheme author: Seth Wright (http://sethawright.com) + +(define-base16-color-theme "google-dark" + :base00 "#1d1f21" + :base01 "#282a2e" + :base02 "#373b41" + :base03 "#969896" + :base04 "#b4b7b4" + :base05 "#c5c8c6" + :base06 "#e0e0e0" + :base07 "#ffffff" + :base08 "#CC342B" + :base09 "#F96A38" + :base0A "#FBA922" + :base0B "#198844" + :base0C "#3971ED" + :base0D "#3971ED" + :base0E "#A36AC7" + :base0F "#3971ED") + +;; Scheme: google-light +;; Scheme author: Seth Wright (http://sethawright.com) + +(define-base16-color-theme "google-light" + :base00 "#ffffff" + :base01 "#e0e0e0" + :base02 "#c5c8c6" + :base03 "#b4b7b4" + :base04 "#969896" + :base05 "#373b41" + :base06 "#282a2e" + :base07 "#1d1f21" + :base08 "#CC342B" + :base09 "#F96A38" + :base0A "#FBA922" + :base0B "#198844" + :base0C "#3971ED" + :base0D "#3971ED" + :base0E "#A36AC7" + :base0F "#3971ED") + +;; Scheme: grayscale-dark +;; Scheme author: Alexandre Gavioli (https://github.com/Alexx2/) + +(define-base16-color-theme "grayscale-dark" + :base00 "#101010" + :base01 "#252525" + :base02 "#464646" + :base03 "#525252" + :base04 "#ababab" + :base05 "#b9b9b9" + :base06 "#e3e3e3" + :base07 "#f7f7f7" + :base08 "#7c7c7c" + :base09 "#999999" + :base0A "#a0a0a0" + :base0B "#8e8e8e" + :base0C "#868686" + :base0D "#686868" + :base0E "#747474" + :base0F "#5e5e5e") + +;; Scheme: grayscale-light +;; Scheme author: Alexandre Gavioli (https://github.com/Alexx2/) + +(define-base16-color-theme "grayscale-light" + :base00 "#f7f7f7" + :base01 "#e3e3e3" + :base02 "#b9b9b9" + :base03 "#ababab" + :base04 "#525252" + :base05 "#464646" + :base06 "#252525" + :base07 "#101010" + :base08 "#7c7c7c" + :base09 "#999999" + :base0A "#a0a0a0" + :base0B "#8e8e8e" + :base0C "#868686" + :base0D "#686868" + :base0E "#747474" + :base0F "#5e5e5e") + +;; Scheme: greenscreen +;; Scheme author: Chris Kempson (http://chriskempson.com) + +(define-base16-color-theme "greenscreen" + :base00 "#001100" + :base01 "#003300" + :base02 "#005500" + :base03 "#007700" + :base04 "#009900" + :base05 "#00bb00" + :base06 "#00dd00" + :base07 "#00ff00" + :base08 "#007700" + :base09 "#009900" + :base0A "#007700" + :base0B "#00bb00" + :base0C "#005500" + :base0D "#009900" + :base0E "#00bb00" + :base0F "#005500") + +;; Scheme: harmonic-dark +;; Scheme author: Jannik Siebert (https://github.com/janniks) + +(define-base16-color-theme "harmonic-dark" + :base00 "#0b1c2c" + :base01 "#223b54" + :base02 "#405c79" + :base03 "#627e99" + :base04 "#aabcce" + :base05 "#cbd6e2" + :base06 "#e5ebf1" + :base07 "#f7f9fb" + :base08 "#bf8b56" + :base09 "#bfbf56" + :base0A "#8bbf56" + :base0B "#56bf8b" + :base0C "#568bbf" + :base0D "#8b56bf" + :base0E "#bf568b" + :base0F "#bf5656") + +;; Scheme: harmonic-light +;; Scheme author: Jannik Siebert (https://github.com/janniks) + +(define-base16-color-theme "harmonic-light" + :base00 "#f7f9fb" + :base01 "#e5ebf1" + :base02 "#cbd6e2" + :base03 "#aabcce" + :base04 "#627e99" + :base05 "#405c79" + :base06 "#223b54" + :base07 "#0b1c2c" + :base08 "#bf8b56" + :base09 "#bfbf56" + :base0A "#8bbf56" + :base0B "#56bf8b" + :base0C "#568bbf" + :base0D "#8b56bf" + :base0E "#bf568b" + :base0F "#bf5656") + +;; Scheme: hopscotch +;; Scheme author: Jan T. Sott + +(define-base16-color-theme "hopscotch" + :base00 "#322931" + :base01 "#433b42" + :base02 "#5c545b" + :base03 "#797379" + :base04 "#989498" + :base05 "#b9b5b8" + :base06 "#d5d3d5" + :base07 "#ffffff" + :base08 "#dd464c" + :base09 "#fd8b19" + :base0A "#fdcc59" + :base0B "#8fc13e" + :base0C "#149b93" + :base0D "#1290bf" + :base0E "#c85e7c" + :base0F "#b33508") + +;; Scheme: irblack +;; Scheme author: Timothée Poisot (http://timotheepoisot.fr) + +(define-base16-color-theme "irblack" + :base00 "#000000" + :base01 "#242422" + :base02 "#484844" + :base03 "#6c6c66" + :base04 "#918f88" + :base05 "#b5b3aa" + :base06 "#d9d7cc" + :base07 "#fdfbee" + :base08 "#ff6c60" + :base09 "#e9c062" + :base0A "#ffffb6" + :base0B "#a8ff60" + :base0C "#c6c5fe" + :base0D "#96cbfe" + :base0E "#ff73fd" + :base0F "#b18a3d") + +;; Scheme: isotope +;; Scheme author: Jan T. Sott + +(define-base16-color-theme "isotope" + :base00 "#000000" + :base01 "#404040" + :base02 "#606060" + :base03 "#808080" + :base04 "#c0c0c0" + :base05 "#d0d0d0" + :base06 "#e0e0e0" + :base07 "#ffffff" + :base08 "#ff0000" + :base09 "#ff9900" + :base0A "#ff0099" + :base0B "#33ff00" + :base0C "#00ffff" + :base0D "#0066ff" + :base0E "#cc00ff" + :base0F "#3300ff") + +;; Scheme: macintosh +;; Scheme author: Rebecca Bettencourt (http://www.kreativekorp.com) + +(define-base16-color-theme "macintosh" + :base00 "#000000" + :base01 "#404040" + :base02 "#404040" + :base03 "#808080" + :base04 "#808080" + :base05 "#c0c0c0" + :base06 "#c0c0c0" + :base07 "#ffffff" + :base08 "#dd0907" + :base09 "#ff6403" + :base0A "#fbf305" + :base0B "#1fb714" + :base0C "#02abea" + :base0D "#0000d3" + :base0E "#4700a5" + :base0F "#90713a") + +;; Scheme: marrakesh +;; Scheme author: Alexandre Gavioli (http://github.com/Alexx2/) + +(define-base16-color-theme "marrakesh" + :base00 "#201602" + :base01 "#302e00" + :base02 "#5f5b17" + :base03 "#6c6823" + :base04 "#86813b" + :base05 "#948e48" + :base06 "#ccc37a" + :base07 "#faf0a5" + :base08 "#c35359" + :base09 "#b36144" + :base0A "#a88339" + :base0B "#18974e" + :base0C "#75a738" + :base0D "#477ca1" + :base0E "#8868b3" + :base0F "#b3588e") + +;; Scheme: monokai +;; Scheme author: Wimer Hazenberg (http://www.monokai.nl) + +(define-base16-color-theme "monokai" + :base00 "#272822" + :base01 "#383830" + :base02 "#49483e" + :base03 "#75715e" + :base04 "#a59f85" + :base05 "#f8f8f2" + :base06 "#f5f4f1" + :base07 "#f9f8f5" + :base08 "#f92672" + :base09 "#fd971f" + :base0A "#f4bf75" + :base0B "#a6e22e" + :base0C "#a1efe4" + :base0D "#66d9ef" + :base0E "#ae81ff" + :base0F "#cc6633") + +;; Scheme: oceanicnext +;; Scheme author: https://github.com/voronianski/oceanic-next-color-scheme + +(define-base16-color-theme "oceanicnext" + :base00 "#1B2B34" + :base01 "#343D46" + :base02 "#4F5B66" + :base03 "#65737E" + :base04 "#A7ADBA" + :base05 "#C0C5CE" + :base06 "#CDD3DE" + :base07 "#D8DEE9" + :base08 "#EC5f67" + :base09 "#F99157" + :base0A "#FAC863" + :base0B "#99C794" + :base0C "#5FB3B3" + :base0D "#6699CC" + :base0E "#C594C5" + :base0F "#AB7967") + +;; Scheme: paraiso +;; Scheme author: Jan T. Sott + +(define-base16-color-theme "paraiso" + :base00 "#2f1e2e" + :base01 "#41323f" + :base02 "#4f424c" + :base03 "#776e71" + :base04 "#8d8687" + :base05 "#a39e9b" + :base06 "#b9b6b0" + :base07 "#e7e9db" + :base08 "#ef6155" + :base09 "#f99b15" + :base0A "#fec418" + :base0B "#48b685" + :base0C "#5bc4bf" + :base0D "#06b6ef" + :base0E "#815ba4" + :base0F "#e96ba8") + +;; Scheme: phd +;; Scheme author: Hennig Hasemann (http://leetless.de/vim.html) + +(define-base16-color-theme "phd" + :base00 "#061229" + :base01 "#2a3448" + :base02 "#4d5666" + :base03 "#717885" + :base04 "#9a99a3" + :base05 "#b8bbc2" + :base06 "#dbdde0" + :base07 "#ffffff" + :base08 "#d07346" + :base09 "#f0a000" + :base0A "#fbd461" + :base0B "#99bf52" + :base0C "#72b9bf" + :base0D "#5299bf" + :base0E "#9989cc" + :base0F "#b08060") + +;; Scheme: pico +;; Scheme author: PICO-8 (http://www.lexaloffle.com/pico-8.php) + +(define-base16-color-theme "pico" + :base00 "#000000" + :base01 "#1d2b53" + :base02 "#7e2553" + :base03 "#008751" + :base04 "#ab5236" + :base05 "#5f574f" + :base06 "#c2c3c7" + :base07 "#fff1e8" + :base08 "#ff004d" + :base09 "#ffa300" + :base0A "#fff024" + :base0B "#00e756" + :base0C "#29adff" + :base0D "#83769c" + :base0E "#ff77a8" + :base0F "#ffccaa") + +;; Scheme: pop +;; Scheme author: Chris Kempson (http://chriskempson.com) + +(define-base16-color-theme "pop" + :base00 "#000000" + :base01 "#202020" + :base02 "#303030" + :base03 "#505050" + :base04 "#b0b0b0" + :base05 "#d0d0d0" + :base06 "#e0e0e0" + :base07 "#ffffff" + :base08 "#eb008a" + :base09 "#f29333" + :base0A "#f8ca12" + :base0B "#37b349" + :base0C "#00aabb" + :base0D "#0e5a94" + :base0E "#b31e8d" + :base0F "#7a2d00") + +;; Scheme: railscasts +;; Scheme author: Ryan Bates (http://railscasts.com) + +(define-base16-color-theme "railscasts" + :base00 "#2b2b2b" + :base01 "#272935" + :base02 "#3a4055" + :base03 "#5a647e" + :base04 "#d4cfc9" + :base05 "#e6e1dc" + :base06 "#f4f1ed" + :base07 "#f9f7f3" + :base08 "#da4939" + :base09 "#cc7833" + :base0A "#ffc66d" + :base0B "#a5c261" + :base0C "#519f50" + :base0D "#6d9cbe" + :base0E "#b6b3eb" + :base0F "#bc9458") + +;; Scheme: seti +;; Scheme author: + +(define-base16-color-theme "seti" + :base00 "#151718" + :base01 "#282a2b" + :base02 "#3B758C" + :base03 "#41535B" + :base04 "#43a5d5" + :base05 "#d6d6d6" + :base06 "#eeeeee" + :base07 "#ffffff" + :base08 "#Cd3f45" + :base09 "#db7b55" + :base0A "#e6cd69" + :base0B "#9fca56" + :base0C "#55dbbe" + :base0D "#55b5db" + :base0E "#a074c4" + :base0F "#8a553f") + +;; Scheme: shapeshifter +;; Scheme author: Tyler Benziger (http://tybenz.com) + +(define-base16-color-theme "shapeshifter" + :base00 "#f9f9f9" + :base01 "#e0e0e0" + :base02 "#ababab" + :base03 "#555555" + :base04 "#343434" + :base05 "#102015" + :base06 "#040404" + :base07 "#000000" + :base08 "#e92f2f" + :base09 "#e09448" + :base0A "#dddd13" + :base0B "#0ed839" + :base0C "#23edda" + :base0D "#3b48e3" + :base0E "#f996e2" + :base0F "#69542d") + +;; Scheme: spacemacs +;; Scheme author: Nasser Alshammari (https://github.com/nashamri/spacemacs-theme) + +(define-base16-color-theme "spacemacs" + :base00 "#1f2022" + :base01 "#282828" + :base02 "#444155" + :base03 "#585858" + :base04 "#b8b8b8" + :base05 "#a3a3a3" + :base06 "#e8e8e8" + :base07 "#f8f8f8" + :base08 "#f2241f" + :base09 "#ffa500" + :base0A "#b1951d" + :base0B "#67b11d" + :base0C "#2d9574" + :base0D "#4f97d7" + :base0E "#a31db1" + :base0F "#b03060") + +;; Scheme: tube +;; Scheme author: Jan T. Sott + +(define-base16-color-theme "tube" + :base00 "#231f20" + :base01 "#1c3f95" + :base02 "#5a5758" + :base03 "#737171" + :base04 "#959ca1" + :base05 "#d9d8d8" + :base06 "#e7e7e8" + :base07 "#ffffff" + :base08 "#ee2e24" + :base09 "#f386a1" + :base0A "#ffd204" + :base0B "#00853e" + :base0C "#85cebc" + :base0D "#009ddc" + :base0E "#98005d" + :base0F "#b06110") + +;; Scheme: unikitty-dark +;; Scheme author: Josh W Lewis (@joshwlewis) + +(define-base16-color-theme "unikitty-dark" + :base00 "#2e2a31" + :base01 "#4a464d" + :base02 "#666369" + :base03 "#838085" + :base04 "#9f9da2" + :base05 "#bcbabe" + :base06 "#d8d7da" + :base07 "#f5f4f7" + :base08 "#d8137f" + :base09 "#d65407" + :base0A "#dc8a0e" + :base0B "#17ad98" + :base0C "#149bda" + :base0D "#796af5" + :base0E "#bb60ea" + :base0F "#c720ca") + +;; Scheme: unikitty-light +;; Scheme author: Josh W Lewis (@joshwlewis) + +(define-base16-color-theme "unikitty-light" + :base00 "#ffffff" + :base01 "#e1e1e2" + :base02 "#c4c3c5" + :base03 "#a7a5a8" + :base04 "#89878b" + :base05 "#6c696e" + :base06 "#4f4b51" + :base07 "#322d34" + :base08 "#d8137f" + :base09 "#d65407" + :base0A "#dc8a0e" + :base0B "#17ad98" + :base0C "#149bda" + :base0D "#775dff" + :base0E "#aa17e6" + :base0F "#e013d0") + +;; Scheme: vice-alt +;; Scheme author: Thomas Leon Highbaugh + +(define-base16-color-theme "vice-alt" + :base00 "#1b1d24" + :base01 "#282b36" + :base02 "#2f303d" + :base03 "#323643" + :base04 "#3f4859" + :base05 "#555e70" + :base06 "#b2bfd9" + :base07 "#f4f4f7" + :base08 "#ff3d81" + :base09 "#F67544" + :base0A "#ffff73" + :base0B "#44ffdd" + :base0C "#00caff" + :base0D "#2fb1d4" + :base0E "#8265ff" + :base0F "#ff00aa") + +;; Scheme: vice +;; Scheme author: Thomas Leon Highbaugh + +(define-base16-color-theme "vice" + :base00 "#17191E" + :base01 "#22262d" + :base02 "#3c3f4c" + :base03 "#383a47" + :base04 "#555e70" + :base05 "#8b9cbe" + :base06 "#B2BFD9" + :base07 "#f4f4f7" + :base08 "#ff29a8" + :base09 "#85ffe0" + :base0A "#f0ffaa" + :base0B "#0badff" + :base0C "#8265ff" + :base0D "#00eaff" + :base0E "#00f6d9" + :base0F "#ff3d81") + +;; Scheme: vulcan +;; Scheme author: Andrey Varfolomeev + +(define-base16-color-theme "vulcan" + :base00 "#041523" + :base01 "#122339" + :base02 "#003552" + :base03 "#7a5759" + :base04 "#6b6977" + :base05 "#5b778c" + :base06 "#333238" + :base07 "#214d68" + :base08 "#818591" + :base09 "#9198a3" + :base0A "#adb4b9" + :base0B "#977d7c" + :base0C "#977d7c" + :base0D "#977d7c" + :base0E "#9198a3" + :base0F "#977d7c") + +;; Scheme: windows-10-light +;; Scheme author: Fergus Collins (https://github.com/C-Fergus) + +(define-base16-color-theme "windows-10-light" + :base00 "#f2f2f2" + :base01 "#e5e5e5" + :base02 "#d9d9d9" + :base03 "#cccccc" + :base04 "#ababab" + :base05 "#767676" + :base06 "#414141" + :base07 "#0c0c0c" + :base08 "#c50f1f" + :base09 "#f9f1a5" + :base0A "#c19c00" + :base0B "#13a10e" + :base0C "#3a96dd" + :base0D "#0037da" + :base0E "#881798" + :base0F "#16c60c") + +;; Scheme: windows-10 +;; Scheme author: Fergus Collins (https://github.com/C-Fergus) + +(define-base16-color-theme "windows-10" + :base00 "#0c0c0c" + :base01 "#2f2f2f" + :base02 "#535353" + :base03 "#767676" + :base04 "#b9b9b9" + :base05 "#cccccc" + :base06 "#dfdfdf" + :base07 "#f2f2f2" + :base08 "#e74856" + :base09 "#c19c00" + :base0A "#f9f1a5" + :base0B "#16c60c" + :base0C "#61d6d6" + :base0D "#3b78ff" + :base0E "#b4009e" + :base0F "#13a10e") + +;; Scheme: windows-95-light +;; Scheme author: Fergus Collins (https://github.com/C-Fergus) + +(define-base16-color-theme "windows-95-light" + :base00 "#fcfcfc" + :base01 "#e0e0e0" + :base02 "#c4c4c4" + :base03 "#a8a8a8" + :base04 "#7e7e7e" + :base05 "#545454" + :base06 "#2a2a2a" + :base07 "#000000" + :base08 "#a80000" + :base09 "#fcfc54" + :base0A "#a85400" + :base0B "#00a800" + :base0C "#00a8a8" + :base0D "#0000a8" + :base0E "#a800a8" + :base0F "#54fc54") + +;; Scheme: windows-95 +;; Scheme author: Fergus Collins (https://github.com/C-Fergus) + +(define-base16-color-theme "windows-95" + :base00 "#000000" + :base01 "#1C1C1C" + :base02 "#383838" + :base03 "#545454" + :base04 "#7e7e7e" + :base05 "#a8a8a8" + :base06 "#d2d2d2" + :base07 "#fcfcfc" + :base08 "#fc5454" + :base09 "#a85400" + :base0A "#fcfc54" + :base0B "#54fc54" + :base0C "#54fcfc" + :base0D "#5454fc" + :base0E "#fc54fc" + :base0F "#00a800") + +;; Scheme: windows-highcontrast-light +;; Scheme author: Fergus Collins (https://github.com/C-Fergus) + +(define-base16-color-theme "windows-highcontrast-light" + :base00 "#fcfcfc" + :base01 "#e8e8e8" + :base02 "#d4d4d4" + :base03 "#c0c0c0" + :base04 "#7e7e7e" + :base05 "#545454" + :base06 "#2a2a2a" + :base07 "#000000" + :base08 "#800000" + :base09 "#fcfc54" + :base0A "#808000" + :base0B "#008000" + :base0C "#008080" + :base0D "#000080" + :base0E "#800080" + :base0F "#54fc54") + +;; Scheme: windows-highcontrast +;; Scheme author: Fergus Collins (https://github.com/C-Fergus) + +(define-base16-color-theme "windows-highcontrast" + :base00 "#000000" + :base01 "#1C1C1C" + :base02 "#383838" + :base03 "#545454" + :base04 "#a2a2a2" + :base05 "#c0c0c0" + :base06 "#dedede" + :base07 "#fcfcfc" + :base08 "#fc5454" + :base09 "#808000" + :base0A "#fcfc54" + :base0B "#54fc54" + :base0C "#54fcfc" + :base0D "#5454fc" + :base0E "#fc54fc" + :base0F "#008000") + +;; Scheme: windows-nt-light +;; Scheme author: Fergus Collins (https://github.com/C-Fergus) + +(define-base16-color-theme "windows-nt-light" + :base00 "#ffffff" + :base01 "#eaeaea" + :base02 "#d5d5d5" + :base03 "#c0c0c0" + :base04 "#a0a0a0" + :base05 "#808080" + :base06 "#404040" + :base07 "#000000" + :base08 "#800000" + :base09 "#ffff00" + :base0A "#808000" + :base0B "#008000" + :base0C "#008080" + :base0D "#000080" + :base0E "#800080" + :base0F "#00ff00") + +;; Scheme: windows-nt +;; Scheme author: Fergus Collins (https://github.com/C-Fergus) + +(define-base16-color-theme "windows-nt" + :base00 "#000000" + :base01 "#2a2a2a" + :base02 "#555555" + :base03 "#808080" + :base04 "#a1a1a1" + :base05 "#c0c0c0" + :base06 "#e0e0e0" + :base07 "#ffffff" + :base08 "#ff0000" + :base09 "#808000" + :base0A "#ffff00" + :base0B "#00ff00" + :base0C "#00ffff" + :base0D "#0000ff" + :base0E "#ff00ff" + :base0F "#008000") + +;; Scheme: woodland +;; Scheme author: Jay Cornwall (https://jcornwall.com) + +(define-base16-color-theme "woodland" + :base00 "#231e18" + :base01 "#302b25" + :base02 "#48413a" + :base03 "#9d8b70" + :base04 "#b4a490" + :base05 "#cabcb1" + :base06 "#d7c8bc" + :base07 "#e4d4c8" + :base08 "#d35c5c" + :base09 "#ca7f32" + :base0A "#e0ac16" + :base0B "#b7ba53" + :base0C "#6eb958" + :base0D "#88a4d3" + :base0E "#bb90e2" + :base0F "#b49368") + +;; Scheme: xcode-dusk +;; Scheme author: Elsa Gonsiorowski (https://github.com/gonsie) + +(define-base16-color-theme "xcode-dusk" + :base00 "#282B35" + :base01 "#3D4048" + :base02 "#53555D" + :base03 "#686A71" + :base04 "#7E8086" + :base05 "#939599" + :base06 "#A9AAAE" + :base07 "#BEBFC2" + :base08 "#B21889" + :base09 "#786DC5" + :base0A "#438288" + :base0B "#DF0002" + :base0C "#00A0BE" + :base0D "#790EAD" + :base0E "#B21889" + :base0F "#C77C48") + +;; Scheme: zenburn +;; Scheme author: elnawe + +(define-base16-color-theme "zenburn" + :base00 "#383838" + :base01 "#404040" + :base02 "#606060" + :base03 "#6f6f6f" + :base04 "#808080" + :base05 "#dcdccc" + :base06 "#c0c0c0" + :base07 "#ffffff" + :base08 "#dca3a3" + :base09 "#dfaf8f" + :base0A "#e0cf9f" + :base0B "#5f7f5f" + :base0C "#93e0e3" + :base0D "#7cb8bb" + :base0E "#dc8cc3" + :base0F "#000000") + +;; Scheme: modus-vivendi +;; Scheme author: Protesilaos Stavrou + +(define-base16-color-theme "modus-vivendi" + :base00 "#000000" + :base01 "#535353" + :base02 "#5a5a5a" + :base03 "#989898" + :base04 "#b6a0ff" + :base05 "#ffffff" + :base06 "#dfcf43" + :base07 "#fefefe" + :base08 "#fec43f" + :base09 "#00bcff" + :base0A "#6ae4b9" + :base0B "#79a8ff" + :base0C "#f78fe7" + :base0D "#feacd0" + :base0E "#b6a0ff" + :base0F "#12154a") diff --git a/extensions/lem-base16-themes/templates/default.mustache b/extensions/lem-base16-themes/templates/default.mustache new file mode 100644 index 000000000..b4c5ab847 --- /dev/null +++ b/extensions/lem-base16-themes/templates/default.mustache @@ -0,0 +1,20 @@ +;; Scheme: {{scheme-slug}} +;; Scheme author: {{scheme-author}} + +(define-base16-color-theme "{{scheme-slug}}" + :base00 "#{{base00-hex}}" + :base01 "#{{base01-hex}}" + :base02 "#{{base02-hex}}" + :base03 "#{{base03-hex}}" + :base04 "#{{base04-hex}}" + :base05 "#{{base05-hex}}" + :base06 "#{{base06-hex}}" + :base07 "#{{base07-hex}}" + :base08 "#{{base08-hex}}" + :base09 "#{{base09-hex}}" + :base0A "#{{base0A-hex}}" + :base0B "#{{base0B-hex}}" + :base0C "#{{base0C-hex}}" + :base0D "#{{base0D-hex}}" + :base0E "#{{base0E-hex}}" + :base0F "#{{base0F-hex}}") diff --git a/extensions/lem-dashboard/dashboard-items.lisp b/extensions/lem-dashboard/dashboard-items.lisp new file mode 100644 index 000000000..38f44efd6 --- /dev/null +++ b/extensions/lem-dashboard/dashboard-items.lisp @@ -0,0 +1,159 @@ +(in-package :lem-dashboard) + +;; Splash +(defclass dashboard-splash (dashboard-item) + ((splash-texts :initarg :splash-texts :accessor splash-texts + :initform '("Welcome!")) + (selected-splash :initarg :selected-splash :accessor selected-splash + :initform nil)) + (:documentation "Randomly displays one of SPLASH-TEXTS") + (:default-initargs + :item-attribute 'document-text-attribute)) + +(defmethod draw-dashboard-item ((item dashboard-splash) point) + (unless (selected-splash item) + (setf (selected-splash item) + (nth (random (length (splash-texts item))) (splash-texts item)))) + (dolist (line (str:lines (selected-splash item))) + (insert-string point (create-centered-string line) :attribute (item-attribute item)) + (insert-character point #\Newline))) + +;; Url +(defclass dashboard-url (dashboard-item) + ((url :initarg :url :accessor url) + (display-text :initarg :display-text :accessor display-text)) + (:documentation "Creates link/button with DISPLAY-TEXT that opens URL externally.") + (:default-initargs + :item-attribute 'document-text-attribute)) + +(defmethod initialize-instance :after ((item dashboard-url) &key) + (with-slots (url action) item + (setf action (lambda () (open-external-file url))))) + +(defmethod draw-dashboard-item ((item dashboard-url) point) + (button:insert-button point + (create-centered-string (display-text item)) + (lambda () (open-external-file (url item))) + :attribute (item-attribute item))) + +;; Working dir +(defclass dashboard-working-dir (dashboard-item) + () + (:documentation "Prints current working directory") + (:default-initargs + :item-attribute 'document-header4-attribute)) + +(defmethod draw-dashboard-item ((item dashboard-working-dir) point) + (let ((working-dir (format nil "> ~A" (buffer-directory)))) + (insert-string point (create-centered-string working-dir) :attribute 'document-header4-attribute) + (insert-character point #\Newline))) + +;; Footer +(defclass dashboard-footer-message (dashboard-item) + ((messages :initarg :messages :accessor messages :initform '("Happy Coding!")) + (selected-message :initarg :selected-message :accessor selected-message + :initform nil)) + (:documentation "Randomly displays one of the passed-in MESSAGES") + (:default-initargs + :item-attribute 'document-text-attribute)) + +(defmethod draw-dashboard-item ((item dashboard-footer-message) point) + (unless (selected-message item) + (setf (selected-message item) + (nth (random (length (messages item))) (messages item)))) + (insert-string point + (create-centered-string (format nil "> ~A" (selected-message item))) + :attribute (item-attribute item))) + +;; Command +(defclass dashboard-command (dashboard-item) + ((display-text :initarg :display-text :accessor display-text) + (action-command :initarg :action-command :accessor action-command)) + (:documentation "Creates a link/button with DISPLAY-TEXT that executes ACTION-COMMAND when clicked.") + (:default-initargs + :item-attribute 'document-text-attribute)) + +(defmethod initialize-instance :after ((item dashboard-command) &key) + (with-slots (action-command action) item + (setf action (lambda () (funcall action-command))))) + +(defmethod draw-dashboard-item ((item dashboard-command) point) + (button:insert-button point + (create-centered-string (display-text item)) + (lambda () (funcall (action-command item))) + :attribute (item-attribute item))) + +;; Recent projects +(defclass dashboard-recent-projects (dashboard-item) + ((project-count :initarg :project-count :accessor project-count :initform 5)) + (:documentation "Displays a list of recent projects, limited to the last PROJECT-COUNT.") + (:default-initargs + :item-attribute 'document-text-attribute + :action (lambda () + (let* ((point (current-point)) + (project (text-property-at point :project-path))) + (when project + (uiop:with-current-directory (project) + (project:project-find-file project))))))) + +(define-command dashboard-move-to-recent-projects () () + (let ((point (buffer-point (current-buffer)))) + (buffer-start point) + (search-forward-regexp point "Recent Projects") + (line-offset point 2) + (move-to-beginning-of-line))) + +(defmethod draw-dashboard-item ((item dashboard-recent-projects) point) + (let* ((title (format nil "~A Recent Projects (r)" (icon-string "package"))) + (title-line (create-centered-string title))) + (insert-string point title-line :attribute 'document-header1-attribute) + (insert-character point #\Newline) + (insert-character point #\Newline) + (let* ((projects (reverse (project:saved-projects))) + (display-projects (subseq projects 0 (min (project-count item) (length projects))))) + (when display-projects + (let* ((longest-project (reduce #'(lambda (a b) (if (> (length a) (length b)) a b)) display-projects)) + (max-length (length longest-project)) + (left-padding (floor (- (window-width (current-window)) max-length) 2))) + (loop for project in display-projects + do (let ((line-start (copy-point point :temporary))) + (insert-string point (str:fit left-padding " ")) + (insert-string point (format nil "~A~%" project)) + (put-text-property line-start point :project-path project)))))))) + +;; Recent files +(defclass dashboard-recent-files (dashboard-item) + ((file-count :initarg :file-count :accessor file-count :initform 5)) + (:documentation "Displays a list of recent files, limited to the last FILE-COUNT.") + (:default-initargs + :item-attribute 'document-text-attribute + :action (lambda () + (let* ((point (current-point)) + (file (text-property-at point :file-path))) + (when file + (find-file file)))))) + +(define-command dashboard-move-to-recent-files () () + (let ((point (buffer-point (current-buffer)))) + (buffer-start point) + (search-forward-regexp point "Recent Files") + (line-offset point 2) + (move-to-beginning-of-line))) + +(defmethod draw-dashboard-item ((item dashboard-recent-files) point) + (let* ((title (format nil "~A Recent Files (f)" (icon-string "file-text"))) + (title-line (create-centered-string title)) + (recent-files (reverse (history:history-data-list (file:file-history))))) + (insert-string point title-line :attribute 'document-header1-attribute) + (insert-character point #\Newline) + (insert-character point #\Newline) + (let ((display-files (subseq recent-files 0 (min (file-count item) (length recent-files))))) + (when display-files + (let* ((longest-file (reduce #'(lambda (a b) (if (> (length a) (length b)) a b)) display-files)) + (max-length (length longest-file)) + (left-padding (floor (- (window-width (current-window)) max-length) 2))) + (loop for file in display-files + do (let ((line-start (copy-point point :temporary))) + (insert-string point (str:fit left-padding " ")) + (insert-string point (format nil "~A~%" file)) + (put-text-property line-start point :file-path file)))))))) diff --git a/extensions/lem-dashboard/default-dashboard.lisp b/extensions/lem-dashboard/default-dashboard.lisp new file mode 100644 index 000000000..b7b2a67f2 --- /dev/null +++ b/extensions/lem-dashboard/default-dashboard.lisp @@ -0,0 +1,86 @@ +(in-package :lem-dashboard) + +(defvar *default-footer-messages* '("Happy Coding!" + "ほげほげ" + "In Lisp we trust, for bugs we adjust" + "Read, Evaluate, Print, Love" + "May your parentheses always be balanced" + "(setf productivity 'high)" + " load-library tetris" + "Lem Editor Modules? Lisp EMacs? Lem's Not Emacs?" + "(cons 'fun 'programming)")) + +(defvar *default-splash* '(" + ----------------------- + [ Welcome to Lem! ] + ----------------------- + + #+====-=== + ##+-----------= + ########**==------= + ########## %-------= +%#########+==-:---===== +########:::::::::::+ = +######*=++-:::::::::: +#########=:::: +%#######*::::: + #######+::::::: + %######=::::::::: + #######*-:::: + %##*-::::: +")) + +(define-command open-lem-docs () () + (open-external-file "https://lem-project.github.io/usage/usage/")) + +(define-command open-lem-github () () + (open-external-file "https://github.com/lem-project/lem")) + +(defun set-default-dashboard (&key + (project-count 5) + (file-count 5) + (splash *default-splash*) + (footer-messages *default-footer-messages*) + hide-links) + (let ((dashboard-items + (list (make-instance 'dashboard-splash + :item-attribute 'document-metadata-attribute + :splash-texts splash) + (make-instance 'dashboard-working-dir) + (make-instance 'dashboard-recent-projects + :project-count project-count + :bottom-margin 1) + (make-instance 'dashboard-recent-files + :file-count file-count + :bottom-margin 1) + (make-instance 'dashboard-command + :display-text " New Lisp Scratch Buffer (l)" + :action-command 'lem-lisp-mode:lisp-scratch + :item-attribute 'document-header2-attribute + :bottom-margin 2)))) + (unless hide-links + (setf dashboard-items + (append dashboard-items + (list (make-instance 'dashboard-url + :display-text " Getting Started (s)" + :url "https://lem-project.github.io/usage/usage/" + :item-attribute 'document-header3-attribute) + (make-instance 'dashboard-url + :display-text " GitHub (g)" + :url "https://github.com/lem-project/lem" + :item-attribute 'document-header3-attribute + :bottom-margin 2))))) + (setf dashboard-items + (append dashboard-items + (list (make-instance 'dashboard-footer-message + :item-attribute 'document-blockquote-attribute + :messages footer-messages)))) + (set-dashboard dashboard-items))) + +(define-key *dashboard-mode-keymap* "s" 'open-lem-docs) +(define-key *dashboard-mode-keymap* "g" 'open-lem-github) +(define-key *dashboard-mode-keymap* "r" 'dashboard-move-to-recent-projects) +(define-key *dashboard-mode-keymap* "f" 'dashboard-move-to-recent-files) +(define-key *dashboard-mode-keymap* "l" 'lem-lisp-mode/internal:lisp-scratch) + +(set-default-dashboard) diff --git a/extensions/lem-dashboard/lem-dashboard.asd b/extensions/lem-dashboard/lem-dashboard.asd new file mode 100644 index 000000000..3e9a9a601 --- /dev/null +++ b/extensions/lem-dashboard/lem-dashboard.asd @@ -0,0 +1,6 @@ +(defsystem "lem-dashboard" + :depends-on (:lem) + :serial t + :components ((:file "lem-dashboard") + (:file "dashboard-items") + (:file "default-dashboard"))) diff --git a/extensions/lem-dashboard/lem-dashboard.lisp b/extensions/lem-dashboard/lem-dashboard.lisp new file mode 100644 index 000000000..b0c5b9787 --- /dev/null +++ b/extensions/lem-dashboard/lem-dashboard.lisp @@ -0,0 +1,131 @@ +(defpackage :lem-dashboard + (:use :cl :lem) + (:export :open-dashboard + :*dashboard-mode-keymap* + :*dashboard-enable* + :dashboard-move-to-recent-projects + :dashboard-move-to-recent-files + :set-dashboard + :set-default-dashboard + :create-centered-string) + (:local-nicknames (:button :lem/button) + (:history :lem/common/history) + (:project :lem-core/commands/project) + (:file :lem-core/commands/file))) + +(in-package :lem-dashboard) + +(defvar *dashboard-buffer-name* "*dashboard*") +(defvar *dashboard-enable* t) +(defvar *dashboard-mode-keymap* (make-keymap :name '*dashboard-mode-keymap* :parent *global-keymap*)) +(defvar *dashboard-layout* nil + "List of dashboard-item instances; will be drawn in order.") + +(defun create-centered-string (str) + "Creates a 'centered' string by padding with space to fill the screen width halfway." + (let* ((padding (max 0 (floor (- (window-width (current-window)) (length str)) 2))) + (spaces (make-string padding :initial-element #\Space))) + (concatenate 'string spaces str))) + +(defclass dashboard-item () + ((item-attribute + :initarg :item-attribute + :accessor item-attribute + :initform 'document-text-attribute + :documentation "Attribute to use when drawing this item.") + (top-margin + :initarg :top-margin + :accessor top-margin + :initform 0 + :documentation "The amount of vertical space (lines) to apply before the item.") + (bottom-margin + :initarg :bottom-margin + :accessor bottom-margin + :initform 1 + :documentation "The amount of vertical space (lines) to apply after the item.") + (action + :initarg :action + :accessor action + :initform nil + :documentation "Function to execute when is pressed over this item.")) + (:documentation "Base class for all dashboard items.")) + +(defgeneric draw-dashboard-item (item point) + (:documentation "Called to draw the dashboard item.") + (:method :before ((item dashboard-item) point) + (dotimes (i (top-margin item)) + (insert-character point #\Newline))) + (:method :after ((item dashboard-item) point) + (dotimes (i (bottom-margin item)) + (insert-character point #\Newline)))) + +(define-command dashboard-open-selected-item () () + "Execute action on selected dashboard item." + (let* ((point (current-point)) + (item (text-property-at point :dashboard-item))) + (when (and item (action item)) + (funcall (action item))))) + +(defmethod draw-dashboard-item :around ((item dashboard-item) point) + "Inserts a :dashboard-item text property in between the starting and ending POINT, useful for tracking." + (let ((start (copy-point point :temporary))) + (call-next-method) + (let ((end (copy-point point :temporary))) + (put-text-property start end :dashboard-item item) + (delete-point start) + (delete-point end)))) + +(define-major-mode dashboard-mode () + (:name "Dashboard" + :keymap *dashboard-mode-keymap*)) + +(defun create-dashboard-buffer () + "Creates the dashboard buffer." + (or (get-buffer *dashboard-buffer-name*) + (make-buffer *dashboard-buffer-name* + :enable-undo-p nil + :read-only-p t))) + +(defun redraw-dashboard () + "Redraws the dashboard, clearing and redrawing all content while attempting to maintain point position." + (let* ((buffer (create-dashboard-buffer)) + (old-line (line-number-at-point (buffer-point buffer))) + (old-column (point-column (buffer-point buffer)))) + (with-buffer-read-only buffer nil + (erase-buffer buffer) + (let ((point (buffer-point buffer))) + (dolist (item *dashboard-layout*) + (draw-dashboard-item item point))) + (change-buffer-mode buffer 'dashboard-mode) + (move-to-line (buffer-point buffer) old-line) + (move-to-column (buffer-point buffer) old-column)))) + +(define-command open-dashboard () () + "Opens the dashboard if it doesn't exist, or switches to it if it does." + (when *dashboard-enable* + (if (get-buffer *dashboard-buffer-name*) + (switch-to-buffer (get-buffer *dashboard-buffer-name*)) + (progn + (redraw-dashboard) + (switch-to-buffer (get-buffer *dashboard-buffer-name*)))))) + +(defun set-dashboard (dashboard-items) + "Sets the new dashboard layout to DASHBOARD-ITEMS list and applies new keymap." + (when dashboard-items + (setf *dashboard-layout* dashboard-items) + (when (get-buffer *dashboard-buffer-name*) + (redraw-dashboard)))) + +(defun handle-resize (window) + "Handle resizing; in this case, redraw the dashboard to keep it centered." + (when (string= (buffer-name (window-buffer window)) *dashboard-buffer-name*) + (redraw-dashboard))) + +(define-key *dashboard-mode-keymap* "n" 'next-line) +(define-key *dashboard-mode-keymap* "p" 'previous-line) +(define-key *dashboard-mode-keymap* "j" 'next-line) +(define-key *dashboard-mode-keymap* "k" 'previous-line) +(define-key *dashboard-mode-keymap* "Return" 'dashboard-open-selected-item) + +(setf lem:*splash-function* #'open-dashboard) +(add-hook *window-size-change-functions* 'handle-resize) diff --git a/extensions/lisp-mode/swank-protocol.lisp b/extensions/lisp-mode/connection.lisp similarity index 75% rename from extensions/lisp-mode/swank-protocol.lisp rename to extensions/lisp-mode/connection.lisp index c13956e3d..2c75b6048 100644 --- a/extensions/lisp-mode/swank-protocol.lisp +++ b/extensions/lisp-mode/connection.lisp @@ -1,25 +1,34 @@ -(defpackage :lem-lisp-mode/swank-protocol - (:use :cl :lem-lisp-mode/errors) +(uiop:define-package :lem-lisp-mode/connection + (:use :cl + :lem-lisp-mode/errors) (:import-from :trivial-types :association-list :proper-list) + (:import-from :lem-lisp-mode/rpc + :write-message-to-stream + :read-message-from-stream) + (:import-from :lem-lisp-mode/reader + :read-from-string*) + (:export :current-connection) + (:export :*broadcast* + :with-broadcast-connections) (:export :connection :connection-hostname :connection-port - :connection-request-count :connection-package :connection-prompt-string :connection-features :connection-command :connection-process :connection-process-directory - :connection-plist) + :connection-plist + :self-connection-p) (:export :new-connection - :log-message :read-message-string :send-message-string :send-message :message-waiting-p + :new-request-id :remote-eval-from-string :remote-eval :finish-evaluated @@ -35,9 +44,8 @@ :connection-machine-type :connection-machine-version :connection-swank-version - :connection-value) - (:documentation "Low-level implementation of a client for the Swank protocol.")) -(in-package :lem-lisp-mode/swank-protocol) + :connection-value)) +(in-package :lem-lisp-mode/connection) (defmacro with-swank-syntax (() &body body) `(with-standard-io-syntax @@ -46,44 +54,19 @@ (*print-readably* nil)) ,@body))) -;;; Encoding and decoding messages +(defvar *connection* nil) -(defun encode-integer (integer) - "Encode an integer to a 0-padded 16-bit hexadecimal string." - (babel:string-to-octets (format nil "~6,'0,X" integer))) +(defun current-connection () + *connection*) -(defun decode-integer (string) - "Decode a string representing a 0-padded 16-bit hex string to an integer." - (parse-integer string :radix 16)) +(defun (setf current-connection) (connection) + (setf *connection* connection)) -;; Writing and reading messages to/from streams +(defgeneric self-connection-p (connection)) -(defun write-message-to-stream (stream message) - "Write a string to a stream, prefixing it with length information for Swank." - (let* ((octets (babel:string-to-octets message)) - (length-octets (encode-integer (length octets))) - (msg (make-array (+ (length length-octets) - (length octets)) - :element-type '(unsigned-byte 8)))) - (replace msg length-octets) - (replace msg octets :start1 (length length-octets)) - (write-sequence msg stream))) +(defclass () ()) -(defun read-message-from-stream (stream) - "Read a string from a string. - -Parses length information to determine how many characters to read." - (let ((length-buffer (make-array 6 :element-type '(unsigned-byte 8)))) - (when (/= 6 (read-sequence length-buffer stream)) - (error 'disconnected)) - (let* ((length (decode-integer (babel:octets-to-string length-buffer))) - (buffer (make-array length :element-type '(unsigned-byte 8)))) - (read-sequence buffer stream) - (babel:octets-to-string buffer)))) - -;;; Data - -(defclass connection () +(defclass connection () ((hostname :reader connection-hostname :initarg :hostname @@ -99,11 +82,6 @@ Parses length information to determine how many characters to read." :initarg :socket :type usocket:stream-usocket :documentation "The usocket socket.") - (request-count - :accessor connection-request-count - :initform 0 - :type integer - :documentation "A number that is increased and sent along with every request.") (package :accessor connection-package :initform "COMMON-LISP-USER" @@ -149,6 +127,9 @@ Parses length information to determine how many characters to read." (plist :initform nil :accessor connection-plist)) (:documentation "A connection to a remote Lisp.")) +(defmethod self-connection-p ((connection )) + nil) + (defmethod connection-value ((connection connection) key) (getf (connection-plist connection) key)) @@ -165,6 +146,12 @@ Parses length information to determine how many characters to read." :port port :socket socket))) (setup connection) + + (when (and (member (connection-hostname connection) '("127.0.0.1" "localhost") :test 'equal) + (equal (connection-pid connection) + (micros/backend:getpid))) + (change-class connection 'self-connection)) + connection)) (defun read-return-message (connection &key (timeout 5)) @@ -226,12 +213,6 @@ Parses length information to determine how many characters to read." (read-all-messages connection) (log:debug "Setup is done now")) -(defvar *event-log* '()) - -(defun log-message (string) - "Log a message." - (push string *event-log*)) - (defun read-message-string (connection) "Read a message string from a Swank connection. @@ -287,8 +268,10 @@ to check if input is available." ;;; Sending messages -(defun new-request-id (connection) - (incf (connection-request-count connection))) +(defvar *request-id-counter* 0) + +(defun new-request-id () + (incf *request-id-counter*)) (defun remote-eval-from-string (connection string @@ -296,7 +279,7 @@ to check if input is available." thread package request-id) - (let* ((request-id (or request-id (new-request-id connection))) + (let* ((request-id (or request-id (new-request-id))) (msg (format nil "(:emacs-rex ~A ~S ~A ~A)" string @@ -347,55 +330,6 @@ to check if input is available." ;;; Reading/parsing messages -(defun read-atom (in) - (let ((token - (coerce (loop :for c := (peek-char nil in nil) - :until (or (null c) (member c '(#\( #\) #\space #\newline #\tab))) - :collect c - :do (read-char in)) - 'string))) - (handler-case (values (read-from-string token) nil) - (error () - (ppcre:register-groups-bind (prefix name) ("(.*?)::?(.*)" token) - (values (intern (string-upcase (string-left-trim ":" name)) - :keyword) - (when prefix - (read-from-string prefix)))))))) - -(defun read-list (in) - (read-char in) - (loop :until (eql (peek-char t in) #\)) - :collect (read-ahead in) - :finally (read-char in))) - -(defun read-sharp (in) - (read-char in) - (case (peek-char nil in) - ((#\() - (let ((list (read-list in))) - (make-array (length list) :initial-contents list))) - ((#\\) - (read-char in) - (read-char in)) - (otherwise - (unread-char #\# in)))) - -(defun read-ahead (in) - (let ((c (peek-char t in))) - (case c - ((#\() - (read-list in)) - ((#\") - (read in)) - ((#\#) - (read-sharp in)) - (otherwise - (read-atom in))))) - -(defun read-from-string* (string) - (with-input-from-string (in string) - (read-ahead in))) - (defun read-message (connection) "Read an arbitrary message from a connection." (with-swank-syntax () @@ -404,3 +338,27 @@ to check if input is available." (defun read-all-messages (connection) (loop while (message-waiting-p connection) collecting (read-message connection))) + + +;;; self connection + +(defclass self-connection (connection) + ()) + +(defmethod self-connection-p ((connection self-connection)) + t) + +;;; broadcast + +(defvar *broadcast* nil) + +(defun call-with-broadcast-connections (function) + (if (or (self-connection-p (current-connection)) + (not *broadcast*)) + (funcall function (current-connection)) + (dolist (connection (lem-lisp-mode/connections:connection-list)) + (unless (self-connection-p connection) + (funcall function connection))))) + +(defmacro with-broadcast-connections ((connection) &body body) + `(call-with-broadcast-connections (lambda (,connection) ,@body))) diff --git a/extensions/lisp-mode/autodoc.lisp b/extensions/lisp-mode/ext/autodoc.lisp similarity index 95% rename from extensions/lisp-mode/autodoc.lisp rename to extensions/lisp-mode/ext/autodoc.lisp index 9fa0c5a86..ad17305a8 100644 --- a/extensions/lisp-mode/autodoc.lisp +++ b/extensions/lisp-mode/ext/autodoc.lisp @@ -40,6 +40,7 @@ (erase-buffer buffer) (change-buffer-mode buffer 'lisp-mode) (insert-string point doc) + (change-buffer-mode buffer 'lem/buffer/fundamental-mode:fundamental-mode) (setf (variable-value 'line-wrap :buffer buffer) nil) (highlighting-marker point) (funcall function buffer)))))))))) diff --git a/extensions/lisp-mode/class-browser.lisp b/extensions/lisp-mode/ext/class-browser.lisp similarity index 100% rename from extensions/lisp-mode/class-browser.lisp rename to extensions/lisp-mode/ext/class-browser.lisp diff --git a/extensions/lisp-mode/completion.lisp b/extensions/lisp-mode/ext/completion.lisp similarity index 100% rename from extensions/lisp-mode/completion.lisp rename to extensions/lisp-mode/ext/completion.lisp diff --git a/extensions/lisp-mode/connection-list.lisp b/extensions/lisp-mode/ext/connection-list.lisp similarity index 97% rename from extensions/lisp-mode/connection-list.lisp rename to extensions/lisp-mode/ext/connection-list.lisp index 98acb6cca..1cb33f753 100644 --- a/extensions/lisp-mode/connection-list.lisp +++ b/extensions/lisp-mode/ext/connection-list.lisp @@ -1,7 +1,7 @@ (defpackage :lem-lisp-mode/connection-list (:use :cl :lem - :lem-lisp-mode/swank-protocol + :lem-lisp-mode/connection :lem-lisp-mode/internal) (:export :lisp-connection-list)) (in-package :lem-lisp-mode/connection-list) diff --git a/extensions/lisp-mode/defstruct-to-defclass.lisp b/extensions/lisp-mode/ext/defstruct-to-defclass.lisp similarity index 100% rename from extensions/lisp-mode/defstruct-to-defclass.lisp rename to extensions/lisp-mode/ext/defstruct-to-defclass.lisp diff --git a/extensions/lisp-mode/detective.lisp b/extensions/lisp-mode/ext/detective.lisp similarity index 100% rename from extensions/lisp-mode/detective.lisp rename to extensions/lisp-mode/ext/detective.lisp diff --git a/extensions/lisp-mode/eval.lisp b/extensions/lisp-mode/ext/eval.lisp similarity index 89% rename from extensions/lisp-mode/eval.lisp rename to extensions/lisp-mode/ext/eval.lisp index 5e3cad9c3..eb3da59ab 100644 --- a/extensions/lisp-mode/eval.lisp +++ b/extensions/lisp-mode/ext/eval.lisp @@ -3,6 +3,10 @@ (:import-from :lem-lisp-mode/inspector :lisp-inspect :open-inspector) + (:import-from :lem-lisp-mode/connection + :new-request-id + :send-message + :with-broadcast-connections) (:export :redisplay-evaluated-message)) (in-package :lem-lisp-mode/eval) @@ -75,7 +79,7 @@ ;; copied from src/display.lisp, TODO: extract this utils (defun compute-evaluated-background-color () - (let ((color (parse-color (lem-core::background-color)))) + (let ((color (parse-color (background-color)))) (multiple-value-bind (h s v) (rgb-to-hsv (color-red color) (color-green color) @@ -83,8 +87,8 @@ (multiple-value-bind (r g b) (hsv-to-rgb h s - (+ v 5)) - (format nil "#~X~X~X" r g b))))) + (+ v (if (< v 50) 5 -5))) + (color-to-hex-string (make-color r g b)))))) (defun display-evaluated-message (start @@ -143,19 +147,22 @@ (remove-eval-result-overlay-between start end) (let ((spinner (lem/loading-spinner:start-loading-spinner :region :start start :end end)) (string (points-to-string start end)) - (request-id (lem-lisp-mode/swank-protocol::new-request-id (current-connection)))) + (request-id (new-request-id))) (setf (spinner-eval-request-id spinner) request-id) - (lem-lisp-mode/internal::with-remote-eval - (`(micros/pretty-eval:pretty-eval ,string) :request-id request-id) - (lambda (value) - (alexandria:destructuring-ecase value - ((:ok result) - (destructuring-bind (&key value id) result + (with-broadcast-connections (connection) + (lem-lisp-mode/internal:with-remote-eval + (`(micros/pretty-eval:pretty-eval ,string) + :request-id request-id + :connection connection) + (lambda (value) + (alexandria:destructuring-ecase value + ((:ok result) + (destructuring-bind (&key value id) result + (lem/loading-spinner:stop-loading-spinner spinner) + (display-spinner-message spinner value nil id))) + ((:abort condition) (lem/loading-spinner:stop-loading-spinner spinner) - (display-spinner-message spinner value nil id))) - ((:abort condition) - (lem/loading-spinner:stop-loading-spinner spinner) - (display-spinner-message spinner condition t))))))) + (display-spinner-message spinner condition t)))))))) (defun eval-last-expression (point) (with-point ((start point) @@ -175,8 +182,9 @@ (define-command lisp-eval-interrupt-at-point () () (dolist (spinner (lem/loading-spinner:get-line-spinners (current-point))) (let ((request-id (spinner-eval-request-id spinner))) - (lem-lisp-mode/swank-protocol::send-message (current-connection) - `(:interrupt-thread ,request-id))))) + (with-broadcast-connections (connection) + (send-message connection + `(:interrupt-thread ,request-id)))))) (defun get-evaluation-value-id-at-point (point) (alexandria:when-let* ((overlay (find-overlay point)) diff --git a/extensions/lisp-mode/exporter.lisp b/extensions/lisp-mode/ext/exporter.lisp similarity index 100% rename from extensions/lisp-mode/exporter.lisp rename to extensions/lisp-mode/ext/exporter.lisp diff --git a/extensions/lisp-mode/highlight.lisp b/extensions/lisp-mode/ext/highlight.lisp similarity index 100% rename from extensions/lisp-mode/highlight.lisp rename to extensions/lisp-mode/ext/highlight.lisp diff --git a/extensions/lisp-mode/hyperspec.lisp b/extensions/lisp-mode/ext/hyperspec.lisp similarity index 100% rename from extensions/lisp-mode/hyperspec.lisp rename to extensions/lisp-mode/ext/hyperspec.lisp diff --git a/extensions/lisp-mode/inspector.lisp b/extensions/lisp-mode/ext/inspector.lisp similarity index 98% rename from extensions/lisp-mode/inspector.lisp rename to extensions/lisp-mode/ext/inspector.lisp index 60a8bd9f5..d1dc8bfd0 100644 --- a/extensions/lisp-mode/inspector.lisp +++ b/extensions/lisp-mode/ext/inspector.lisp @@ -60,7 +60,7 @@ (define-key *lisp-inspector-keymap* "M-q" 'lisp-inspector-quit) (define-key *lisp-inspector-keymap* "M-Return" 'lisp-inspector-copy-down-to-repl) -(define-command lisp-inspect (string &key (self-evaluation t) (focus nil)) +(define-command lisp-inspect (string &key (self-evaluation nil) (focus t)) ((or (symbol-string-at-point (current-point)) (prompt-for-sexp "Inspect value (evaluated): "))) (lisp-eval-async (if self-evaluation @@ -293,7 +293,7 @@ (define-message (:inspect what thread tag) (let ((hook (when (and thread tag) (alexandria:curry (lambda (sexp) - (lem-lisp-mode/swank-protocol:send-message-string + (lem-lisp-mode/connection:send-message-string (current-connection) sexp)) `(:emacs-return ,thread ,tag nil))))) diff --git a/extensions/lisp-mode/macroexpand.lisp b/extensions/lisp-mode/ext/macroexpand.lisp similarity index 99% rename from extensions/lisp-mode/macroexpand.lisp rename to extensions/lisp-mode/ext/macroexpand.lisp index f01cc3686..7d7a3dffa 100644 --- a/extensions/lisp-mode/macroexpand.lisp +++ b/extensions/lisp-mode/ext/macroexpand.lisp @@ -3,6 +3,8 @@ :alexandria :lem :lem-lisp-mode/internal) + (:export :lisp-macrostep-expand + :lisp-macroexpand-all) #+sbcl (:lock t)) (in-package :lem-lisp-mode/macroexpand) diff --git a/extensions/lisp-mode/organize-imports.lisp b/extensions/lisp-mode/ext/organize-imports.lisp similarity index 100% rename from extensions/lisp-mode/organize-imports.lisp rename to extensions/lisp-mode/ext/organize-imports.lisp diff --git a/extensions/lisp-mode/package-inferred-system.lisp b/extensions/lisp-mode/ext/package-inferred-system.lisp similarity index 100% rename from extensions/lisp-mode/package-inferred-system.lisp rename to extensions/lisp-mode/ext/package-inferred-system.lisp diff --git a/extensions/lisp-mode/paren-coloring.lisp b/extensions/lisp-mode/ext/paren-coloring.lisp similarity index 74% rename from extensions/lisp-mode/paren-coloring.lisp rename to extensions/lisp-mode/ext/paren-coloring.lisp index 48126a177..b70cc278b 100644 --- a/extensions/lisp-mode/paren-coloring.lisp +++ b/extensions/lisp-mode/ext/paren-coloring.lisp @@ -6,6 +6,12 @@ (:export :paren-coloring :*paren-attribute* :*rainbow* + :paren-color-1 + :paren-color-2 + :paren-color-3 + :paren-color-4 + :paren-color-5 + :paren-color-6 :toggle-paren-coloring)) (in-package :lem-lisp-mode/paren-coloring) @@ -15,40 +21,25 @@ (enable) (disable)))) -(defvar *default-rainbow-colors* #("red" "blue" "green" "sienna4" "dark cyan" "orange")) +(define-attribute paren-color-1 + (t :foreground "red")) -(defun rainbow-color (index) - (let ((color-theme (find-color-theme (current-theme)))) - (if color-theme - (get-color-theme-color color-theme - (ecase index - (0 :base08) - (1 :base09) - (2 :base0a) - (3 :base0b) - (4 :base0c) - (5 :base0d))) - (aref *default-rainbow-colors* index)))) +(define-attribute paren-color-2 + (t :foreground "blue")) -(define-attribute color-1 - (t :foreground (rainbow-color 0))) +(define-attribute paren-color-3 + (t :foreground "green")) -(define-attribute color-2 - (t :foreground (rainbow-color 1))) +(define-attribute paren-color-4 + (t :foreground "sienna4")) -(define-attribute color-3 - (t :foreground (rainbow-color 2))) +(define-attribute paren-color-5 + (t :foreground "dark cyan")) -(define-attribute color-4 - (t :foreground (rainbow-color 3))) +(define-attribute paren-color-6 + (t :foreground "orange")) -(define-attribute color-5 - (t :foreground (rainbow-color 4))) - -(define-attribute color-6 - (t :foreground (rainbow-color 5))) - -(defparameter *rainbow-colors* #(color-1 color-2 color-3 color-4 color-5 color-6)) +(defparameter *rainbow-colors* #(paren-color-1 paren-color-2 paren-color-3 paren-color-4 paren-color-5 paren-color-6)) (defvar *paren-attribute* (make-attribute :foreground "dim gray")) diff --git a/extensions/lisp-mode/quickdocs.lisp b/extensions/lisp-mode/ext/quickdocs.lisp similarity index 100% rename from extensions/lisp-mode/quickdocs.lisp rename to extensions/lisp-mode/ext/quickdocs.lisp diff --git a/extensions/lisp-mode/self-insert-hook.lisp b/extensions/lisp-mode/ext/self-insert-hook.lisp similarity index 100% rename from extensions/lisp-mode/self-insert-hook.lisp rename to extensions/lisp-mode/ext/self-insert-hook.lisp diff --git a/extensions/lisp-mode/sldb.lisp b/extensions/lisp-mode/ext/sldb.lisp similarity index 100% rename from extensions/lisp-mode/sldb.lisp rename to extensions/lisp-mode/ext/sldb.lisp diff --git a/extensions/lisp-mode/test-runner.lisp b/extensions/lisp-mode/ext/test-runner.lisp similarity index 100% rename from extensions/lisp-mode/test-runner.lisp rename to extensions/lisp-mode/ext/test-runner.lisp diff --git a/extensions/lisp-mode/trace.lisp b/extensions/lisp-mode/ext/trace.lisp similarity index 100% rename from extensions/lisp-mode/trace.lisp rename to extensions/lisp-mode/ext/trace.lisp diff --git a/extensions/lisp-mode/utopian.lisp b/extensions/lisp-mode/ext/utopian.lisp similarity index 100% rename from extensions/lisp-mode/utopian.lisp rename to extensions/lisp-mode/ext/utopian.lisp diff --git a/extensions/lisp-mode/grammar.lisp b/extensions/lisp-mode/grammar.lisp index 0b29db641..36303e1b7 100644 --- a/extensions/lisp-mode/grammar.lisp +++ b/extensions/lisp-mode/grammar.lisp @@ -133,6 +133,17 @@ :captures (vector nil (make-tm-name 'syntax-keyword-attribute) (make-tm-name 'syntax-function-name-attribute))) + (make-tm-match + `(:sequence + "(" + (:group :case-insensitive-p + (:register (:sequence "def" symbol))) + (:alternation (:greedy-repetition 1 nil :whitespace-char-class) + :end-anchor) + (:greedy-repetition 0 1 (:register symbol))) + :captures (vector nil + (make-tm-name 'syntax-keyword-attribute) + (make-tm-name 'syntax-function-name-attribute))) (make-tm-match `(:sequence "(" diff --git a/extensions/lisp-mode/internal-package.lisp b/extensions/lisp-mode/internal-package.lisp index 0d3ba3947..2f2678a68 100644 --- a/extensions/lisp-mode/internal-package.lisp +++ b/extensions/lisp-mode/internal-package.lisp @@ -6,13 +6,13 @@ :lem/button :lem/loading-spinner :lem-lisp-mode/errors - :lem-lisp-mode/swank-protocol + :lem-lisp-mode/connection :lem-lisp-mode/connections :lem-lisp-mode/message-dispatcher :lem-lisp-mode/ui-mode :lem-lisp-mode/grammar) (:export - ;; reexport swank-protocol.lisp + ;; reexport micros-protocol.lisp :connection-value) (:export ;; lisp-ui-mode.lisp @@ -41,6 +41,7 @@ :with-remote-eval :lisp-eval-from-string :lisp-eval + :lisp-scratch :lisp-eval-async :eval-with-transcript :re-eval-defvar @@ -67,7 +68,8 @@ :show-description :lisp-eval-describe :lisp-describe-symbol - :connect-to-swank + :connect-to-micros + :connect-to-multiple-servers :slime-connect :show-source-location :source-location-to-xref-location @@ -100,6 +102,8 @@ :lisp-apropos-package ;; message.lisp :display-message + ;; package.lisp + :lisp-listen-in-current-package ;; :self-connection :self-connection-p diff --git a/extensions/lisp-mode/lem-lisp-mode.asd b/extensions/lisp-mode/lem-lisp-mode.asd index 54620258a..f0172d911 100644 --- a/extensions/lisp-mode/lem-lisp-mode.asd +++ b/extensions/lisp-mode/lem-lisp-mode.asd @@ -8,45 +8,51 @@ "lem-lisp-syntax" "lem" "lem-process" - "lem-socket-utils" "lem-lsp-mode") :serial t :components ((:file "test-api") - (:file "exporter") (:file "errors") - (:file "swank-protocol") + (:file "ui-mode") + + (:file "rpc") + (:file "reader") (:file "connections") + (:file "connection") + (:file "message-dispatcher") - (:file "ui-mode") (:file "grammar") (:file "implementation") (:file "internal-package") - (:file "completion") (:file "message") - (:file "detective") (:file "file-conversion") + + (:file "ext/detective") + (:file "ext/completion") + (:file "lisp-mode") (:file "message-definitions") (:file "repl") - (:file "inspector") - (:file "eval") - (:file "sldb") - (:file "hyperspec") (:file "apropos-mode") - (:file "autodoc") - (:file "paren-coloring") - (:file "defstruct-to-defclass") - (:file "quickdocs") - (:file "package-inferred-system") - (:file "organize-imports") - (:file "connection-list") - (:file "self-insert-hook") - (:file "trace") - (:file "class-browser") - (:file "macroexpand") - (:file "test-runner") - (:file "utopian") - (:file "highlight") + + (:file "ext/exporter") + (:file "ext/inspector") + (:file "ext/eval") + (:file "ext/sldb") + (:file "ext/hyperspec") + (:file "ext/autodoc") + (:file "ext/paren-coloring") + (:file "ext/defstruct-to-defclass") + (:file "ext/quickdocs") + (:file "ext/package-inferred-system") + (:file "ext/organize-imports") + (:file "ext/connection-list") + (:file "ext/self-insert-hook") + (:file "ext/trace") + (:file "ext/class-browser") + (:file "ext/macroexpand") + (:file "ext/test-runner") + (:file "ext/utopian") + (:file "ext/highlight") (:file "package"))) (defsystem "lem-lisp-mode/v2" diff --git a/extensions/lisp-mode/lisp-mode.lisp b/extensions/lisp-mode/lisp-mode.lisp index 80ff6bf3e..e56c48ab8 100644 --- a/extensions/lisp-mode/lisp-mode.lisp +++ b/extensions/lisp-mode/lisp-mode.lisp @@ -13,14 +13,6 @@ (defparameter *default-port* 4005) (defparameter *localhost* "127.0.0.1") -(defvar *connection* nil) - -(defun current-connection () - *connection*) - -(defun (setf current-connection) (connection) - (setf *connection* connection)) - (set-syntax-parser lem-lisp-syntax:*syntax-table* (make-tmlanguage-lisp)) @@ -95,13 +87,15 @@ (define-key *lisp-mode-keymap* "C-c C-q" 'lisp-quickload) (define-key *lisp-mode-keymap* "Return" 'newline-and-indent) (define-key *lisp-mode-keymap* "C-c C-j" 'lisp-eval-expression-in-repl) +(define-key *lisp-mode-keymap* "C-c ~" 'lisp-listen-in-current-package) (defmethod convert-modeline-element ((element (eql 'lisp-mode)) window) (format nil " ~A~A" (buffer-package (window-buffer window) "CL-USER") (if (current-connection) (format nil " ~A:~A" (connection-implementation-name (current-connection)) - (or (self-connection-p (current-connection)) + (if (self-connection-p (current-connection)) + "SELF" (connection-pid (current-connection)))) ""))) @@ -157,8 +151,9 @@ :label "Export symbol" :callback (lambda (&rest args) (declare (ignore args)) - (lem-lisp-mode/exporter:lisp-add-export - (symbol-string-at-point point))))))) + (uiop:symbol-call :lem-lisp-mode/exporter + :lisp-add-export + (symbol-string-at-point point))))))) (defun context-menu-browse-class-as-tree () (let ((point (point-over-symbol-with-menu-opened-p))) @@ -188,7 +183,17 @@ (when (current-connection) (abort-all (current-connection) "change connection") (notify-change-connection-to-wait-message-thread)) - (setf (current-connection) connection)) + (setf (current-connection) connection) + (when (repl-buffer) + (write-string-to-repl + (if (self-connection-p connection) + (format nil + "~%; changed connection (self connection)") + (format nil + "~%; changed connection (~A ~A)" + (connection-implementation-name connection) + (connection-implementation-version connection))) + :attribute 'syntax-comment-attribute))) (defmethod switch-connection ((connection connection)) (change-current-connection connection)) @@ -216,11 +221,11 @@ (defun self-connect () (unless lem-lisp-mode/test-api:*disable-self-connect* - (let ((port (lem-socket-utils:random-available-port))) + (let ((port (lem/common/socket:random-available-port))) (log:debug "Starting internal SWANK and connecting to it" micros:*communication-style*) (let ((micros::*swank-debug-p* nil)) (micros:create-server :port port :style :spawn)) - (connect-to-swank *localhost* port) + (connect-to-micros *localhost* port) (update-buffer-package) ;; XXX: @@ -230,14 +235,6 @@ (setf *self-connected-port* port)))) -(defun self-connection-p (connection) - (and (typep connection 'connection) - (integerp (self-connected-port)) - (member (connection-hostname connection) '("127.0.0.1" "localhost") :test 'equal) - (ignore-errors (equal (connection-pid connection) (micros/backend:getpid))) - (= (connection-port connection) (self-connected-port)) - :self)) - (defun self-connection () (find-if #'self-connection-p (connection-list))) @@ -248,7 +245,7 @@ (defun buffer-package (buffer &optional default) (let ((package-name (buffer-value buffer "package" default))) (typecase package-name - (null (alexandria:if-let (package-name (scan-current-package (buffer-point buffer))) + (null (alexandria:if-let (package-name (guess-current-position-package (buffer-point buffer))) (string-upcase package-name) default)) ((or symbol string) @@ -272,7 +269,7 @@ (defun (setf buffer-thread-id) (value buffer) (setf (buffer-value buffer 'thread) value)) -(defun current-swank-thread () +(defun current-micros-thread () (or (buffer-thread-id (current-buffer)) t)) @@ -289,7 +286,7 @@ (defun call-with-remote-eval (form continuation &key (connection (current-connection)) - (thread (current-swank-thread)) + (thread (current-micros-thread)) (package (current-package)) request-id) (remote-eval connection @@ -310,7 +307,7 @@ (defun lisp-eval-internal (emacs-rex-fun rex-arg package) (let ((tag (gensym)) - (thread-id (current-swank-thread))) + (thread-id (current-micros-thread))) (catch tag (funcall emacs-rex-fun (current-connection) @@ -337,29 +334,31 @@ (defun lisp-eval-async (form &optional cont (package (current-package))) (let ((buffer (current-buffer))) - (with-remote-eval (form :package package) + (with-broadcast-connections (connection) + (with-remote-eval (form :package package :connection connection) + (lambda (value) + (alexandria:destructuring-ecase value + ((:ok result) + (when cont + (let ((prev (current-buffer))) + (setf (current-buffer) buffer) + (funcall cont result) + (unless (eq (current-buffer) + (window-buffer (current-window))) + (setf (current-buffer) prev))))) + ((:abort condition) + (display-message "Evaluation aborted on ~A." condition)))))))) + +(defun eval-with-transcript (form &key (package (current-package))) + (with-broadcast-connections (connection) + (with-remote-eval (form :package package :connection connection) (lambda (value) (alexandria:destructuring-ecase value - ((:ok result) - (when cont - (let ((prev (current-buffer))) - (setf (current-buffer) buffer) - (funcall cont result) - (unless (eq (current-buffer) - (window-buffer (current-window))) - (setf (current-buffer) prev))))) + ((:ok x) + (display-message "~A" x)) ((:abort condition) (display-message "Evaluation aborted on ~A." condition))))))) -(defun eval-with-transcript (form &key (package (current-package))) - (with-remote-eval (form :package package) - (lambda (value) - (alexandria:destructuring-ecase value - ((:ok x) - (display-message "~A" x)) - ((:abort condition) - (display-message "Evaluation aborted on ~A." condition)))))) - (defun re-eval-defvar (string) (eval-with-transcript `(micros:re-evaluate-defvar ,string))) @@ -472,14 +471,20 @@ (define-command lisp-listen-in-current-package () () (check-connection) (alexandria:when-let ((repl-buffer (repl-buffer)) - (package (buffer-package (current-buffer)))) + (package (buffer-package (current-buffer))) + (original-buffer (current-buffer))) (save-excursion - (setf (current-buffer) repl-buffer) - (destructuring-bind (name prompt-string) - (lisp-eval `(micros:set-package ,package)) - (new-package name prompt-string))) - (start-lisp-repl) - (buffer-end (buffer-point repl-buffer)))) + (cond ((lisp-eval `(not (null (cl:find-package ,package)))) + (save-excursion + (setf (current-buffer) repl-buffer) + (destructuring-bind (name prompt-string) + (lisp-eval `(micros:set-package ,package)) + (new-package name prompt-string))) + (start-lisp-repl) + (buffer-end (buffer-point repl-buffer))) + (t + (message "Package ~A not found" package) + (setf (current-buffer) original-buffer)))))) (define-command lisp-current-directory () () (message "Current directory: ~a" @@ -494,9 +499,10 @@ (micros/backend:filename-to-pathname ,directory)))) (define-command lisp-interrupt () () - (send-message-string - (current-connection) - (format nil "(:emacs-interrupt ~A)" (current-swank-thread)))) + (with-broadcast-connections (connection) + (send-message-string + connection + (format nil "(:emacs-interrupt ~A)" (current-micros-thread))))) (defun prompt-for-sexp (string &optional initial) (prompt-for-string string @@ -514,7 +520,7 @@ (defun self-current-package () (or (find (or *current-package* (buffer-package (current-buffer)) - (scan-current-package (current-point))) + (guess-current-position-package (current-point))) (list-all-packages) :test 'equalp :key 'package-name) @@ -677,7 +683,7 @@ :do (let* ((pos (xref-location-position xref-location)) (buffer (xref-filespec-to-buffer (xref-location-filespec xref-location)))) (push (make-highlight-overlay pos buffer message source-context) - (buffer-compilation-notes-overlays (current-buffer))))) + (buffer-compilation-notes-overlays buffer)))) (setf (buffer-compilation-notes-timer (current-buffer)) (start-timer (make-idle-timer 'show-compilation-notes :name "lisp-show-compilation-notes") @@ -848,7 +854,7 @@ end)))) ((:abort condition) (editor-error "abort ~A" condition)))) - :thread (current-swank-thread) + :thread (current-micros-thread) :package (current-package))))) (defun describe-symbol (symbol-name) @@ -880,13 +886,18 @@ (defvar *wait-message-thread* nil) (defun notify-change-connection-to-wait-message-thread () - (bt:interrupt-thread *wait-message-thread* + (bt2:interrupt-thread *wait-message-thread* (lambda () (error 'change-connection)))) +(defun message-waiting-some-connections-p (&key (timeout 0)) + (with-broadcast-connections (connection) + (when (message-waiting-p connection :timeout timeout) + (return-from message-waiting-some-connections-p t)))) + (defun start-thread () (unless *wait-message-thread* (setf *wait-message-thread* - (bt:make-thread + (bt2:make-thread (lambda () (loop :named exit :do @@ -902,7 +913,7 @@ (unless (connected-p) (setf *wait-message-thread* nil) (return-from exit)) - (when (message-waiting-p (current-connection) :timeout 1) + (when (message-waiting-some-connections-p :timeout 1) (let ((barrior t)) (send-event (lambda () (unwind-protect (progn (pull-events) @@ -925,7 +936,7 @@ :timeout 1 :style '(:gravity :center))) -(defun connect-to-swank (hostname port) +(defun connect-to-micros (hostname port) (let ((connection (handler-case (if (eq hostname *localhost*) (or (ignore-errors (new-connection "127.0.0.1" port)) @@ -943,26 +954,22 @@ (parse-integer (prompt-for-string "Port: " :initial-value (princ-to-string *default-port*)))))) - (let ((connection (connect-to-swank hostname port))) + (let ((connection (connect-to-micros hostname port))) (when start-repl (start-lisp-repl)) (connected-slime-message connection))) +(defun connect-to-multiple-servers (host-and-ports) + (dolist (host-and-port host-and-ports) + (destructuring-bind (&key host port) host-and-port + (connect-to-micros host port)))) + (defun pull-events () (when (connected-p) - (handler-case (loop :while (message-waiting-p (current-connection)) - :do (dispatch-message (read-message (current-connection)))) - (disconnected () - (remove-and-change-connection (current-connection)))))) - -(defvar *event-hooks* '()) - -(defun dispatch-message (message) - (log-message (prin1-to-string message)) - (dolist (e *event-hooks*) - (when (funcall e message) - (return-from dispatch-message))) - (alexandria:when-let (dispatcher (get-message-dispatcher (first message))) - (funcall dispatcher message))) + (with-broadcast-connections (connection) + (handler-case (loop :while (message-waiting-p connection) + :do (dispatch-message (read-message connection))) + (disconnected () + (remove-and-change-connection connection)))))) (defun read-from-minibuffer (thread tag prompt initial-value) (let ((input (prompt-for-sexp prompt initial-value))) @@ -1060,7 +1067,7 @@ command)) (defun lisp-process-buffer-name (port) - (format nil "*Run Lisp swank/~D*" port)) + (format nil "*Run Lisp/~D*" port)) (defun get-lisp-process-buffer (port) (get-buffer (lisp-process-buffer-name port))) @@ -1081,7 +1088,7 @@ (lem-process:process-send-input process (format nil "(require :asdf)~%")) process))) -(defun send-swank-create-server (process port) +(defun send-micros-create-server (process port) (let ((file (asdf:system-source-file (asdf:find-system :micros)))) (lem-process:process-send-input process @@ -1096,10 +1103,10 @@ (format nil "(micros:create-server :port ~D :dont-close t)~%" port))) (defun run-slime (command &key (directory (buffer-directory))) - (let* ((port (lem-socket-utils:random-available-port)) + (let* ((port (lem/common/socket:random-available-port)) (process (run-lisp :command command :directory directory :port port))) - (send-swank-create-server process port) - (start-lisp-repl) + (send-micros-create-server process port) + (start-lisp-repl-internal :new-process t) (let ((spinner (start-loading-spinner :modeline :buffer (repl-buffer) @@ -1108,7 +1115,7 @@ (retry-count 0)) (labels ((interval () (handler-case - (let ((conn (connect-to-swank *localhost* port))) + (let ((conn (connect-to-micros *localhost* port))) (setf (connection-command conn) command) (setf (connection-process conn) process) (setf (connection-process-directory conn) directory) @@ -1153,7 +1160,7 @@ (lem-process:delete-process (connection-process connection)) t) (remove-and-change-connection connection) - (usocket:socket-close (lem-lisp-mode/swank-protocol::connection-socket connection)))) + (usocket:socket-close (lem-lisp-mode/connection::connection-socket connection)))) (define-command slime-quit () () (when (self-connection-p (current-connection)) @@ -1196,7 +1203,11 @@ (when start-repl (start-lisp-repl))) -(defun scan-current-package (point) +(defun buffer-pathname-type (buffer) + (alexandria:when-let (pathname (buffer-filename buffer)) + (pathname-type pathname))) + +(defun guess-current-position-package (point) (with-point ((p point)) (loop (ppcre:register-groups-bind (package-name) @@ -1204,10 +1215,12 @@ (string-downcase (line-string p))) (return package-name)) (unless (line-offset p -1) - (return))))) + (if (equal "asd" (buffer-pathname-type (point-buffer point))) + (return "ASDF-USER") + (return)))))) (defun update-buffer-package () - (let ((package (scan-current-package (current-point)))) + (let ((package (guess-current-position-package (current-point)))) (when package (lisp-set-package package)))) @@ -1264,7 +1277,7 @@ (progn (sleep 0.5) (dolist (c conn-list) - (let* ((s (lem-lisp-mode/swank-protocol::connection-socket c)) + (let* ((s (lem-lisp-mode/connection::connection-socket c)) (fd (sb-bsd-sockets::socket-file-descriptor (usocket:socket s)))) (ignore-errors ;;(usocket:socket-shutdown s :IO) diff --git a/extensions/lisp-mode/message-definitions.lisp b/extensions/lisp-mode/message-definitions.lisp index d28053f08..eea637a22 100644 --- a/extensions/lisp-mode/message-definitions.lisp +++ b/extensions/lisp-mode/message-definitions.lisp @@ -4,7 +4,8 @@ (declare (ignore target)) (write-string-to-repl string) (when thread - (send-message *connection* `(:write-done ,thread)))) + (with-broadcast-connections (connection) + (send-message connection `(:write-done ,thread))))) (define-message (:write-object string id type) (write-object-to-repl string id type)) @@ -19,7 +20,8 @@ (new-package name prompt-string)) (define-message (:return value id) - (finish-evaluated *connection* value id)) + (with-broadcast-connections (connection) + (finish-evaluated connection value id))) (define-message (:read-from-minibuffer thread tag prompt initial-value) (read-from-minibuffer thread tag prompt initial-value)) @@ -28,16 +30,18 @@ (dispatch-message `(:emacs-return ,thread ,tag ,(prompt-for-y-or-n-p question)))) (define-message (:emacs-return-string thread tag string) - (send-message-string - *connection* - (format nil "(:emacs-return-string ~A ~A ~S)" - thread - tag - string))) + (with-broadcast-connections (connection) + (send-message-string + connection + (format nil "(:emacs-return-string ~A ~A ~S)" + thread + tag + string)))) (define-message (:new-features features) - (setf (connection-features *connection*) - features)) + (with-broadcast-connections (connection) + (setf (connection-features connection) + features))) (define-message (:indentation-update info) (indentation-update info)) @@ -54,15 +58,17 @@ (dispatch-message `(:emacs-return ,thread ,tag ,result)))) (define-message (:emacs-return thread tag value) - (send-message-string - *connection* - (format nil "(:emacs-return ~A ~A ~S)" thread tag value))) + (with-broadcast-connections (connection) + (send-message-string + connection + (format nil "(:emacs-return ~A ~A ~S)" thread tag value)))) (define-message (:debug-condition thread message) (assert thread) (display-message "~A" message)) (define-message (:ping thread tag) - (send-message-string - *connection* - (format nil "(:emacs-pong ~A ~A)" thread tag))) + (with-broadcast-connections (connection) + (send-message-string + connection + (format nil "(:emacs-pong ~A ~A)" thread tag)))) diff --git a/extensions/lisp-mode/message-dispatcher.lisp b/extensions/lisp-mode/message-dispatcher.lisp index 0843e95d9..53a2b369d 100644 --- a/extensions/lisp-mode/message-dispatcher.lisp +++ b/extensions/lisp-mode/message-dispatcher.lisp @@ -1,7 +1,8 @@ (defpackage :lem-lisp-mode/message-dispatcher (:use :cl) (:export :get-message-dispatcher - :define-message)) + :define-message + :dispatch-message)) (in-package :lem-lisp-mode/message-dispatcher) (defvar *message-dispatcher* (make-hash-table :test 'eq)) @@ -18,3 +19,19 @@ ,@body)) (setf (gethash ,name *message-dispatcher*) ',fn-name))))) + +(defvar *event-log* '()) + +(defun log-message (string) + "Log a message." + (push string *event-log*)) + +(defvar *event-hooks* '()) + +(defun dispatch-message (message) + (log-message (prin1-to-string message)) + (dolist (e *event-hooks*) + (when (funcall e message) + (return-from dispatch-message))) + (alexandria:when-let (dispatcher (get-message-dispatcher (first message))) + (funcall dispatcher message))) diff --git a/extensions/lisp-mode/reader.lisp b/extensions/lisp-mode/reader.lisp new file mode 100644 index 000000000..ee9d6ad71 --- /dev/null +++ b/extensions/lisp-mode/reader.lisp @@ -0,0 +1,53 @@ +(uiop:define-package :lem-lisp-mode/reader + (:use :cl) + (:export :read-from-string*)) +(in-package :lem-lisp-mode/reader) + +(defun read-atom (in) + (let ((token + (coerce (loop :for c := (peek-char nil in nil) + :until (or (null c) (member c '(#\( #\) #\space #\newline #\tab))) + :collect c + :do (read-char in)) + 'string))) + (handler-case (values (read-from-string token) nil) + (error () + (ppcre:register-groups-bind (prefix name) ("(.*?)::?(.*)" token) + (values (intern (string-upcase (string-left-trim ":" name)) + :keyword) + (when prefix + (read-from-string prefix)))))))) + +(defun read-list (in) + (read-char in) + (loop :until (eql (peek-char t in) #\)) + :collect (read-ahead in) + :finally (read-char in))) + +(defun read-sharp (in) + (read-char in) + (case (peek-char nil in) + ((#\() + (let ((list (read-list in))) + (make-array (length list) :initial-contents list))) + ((#\\) + (read-char in) + (read-char in)) + (otherwise + (unread-char #\# in)))) + +(defun read-ahead (in) + (let ((c (peek-char t in))) + (case c + ((#\() + (read-list in)) + ((#\") + (read in)) + ((#\#) + (read-sharp in)) + (otherwise + (read-atom in))))) + +(defun read-from-string* (string) + (with-input-from-string (in string) + (read-ahead in))) diff --git a/extensions/lisp-mode/repl.lisp b/extensions/lisp-mode/repl.lisp index b155c2867..9f3e56d50 100644 --- a/extensions/lisp-mode/repl.lisp +++ b/extensions/lisp-mode/repl.lisp @@ -115,7 +115,7 @@ (setf (buffer-value (repl-buffer) 'read-string-tag-stack) val)) (define-command lisp-repl-interrupt () () - (send-message-string *connection* + (send-message-string (current-connection) (format nil "(:emacs-interrupt ~(~S~))" (or (car (read-string-thread-stack)) :repl-thread)))) @@ -148,8 +148,9 @@ buffer)) (defun repl-set-prompt (point) + (update-repl-buffer-write-point point) (insert-string point - (format nil "~A> " (connection-prompt-string *connection*))) + (format nil "~A> " (connection-prompt-string (current-connection)))) point) (defun repl-paren-correspond-p (point) @@ -222,7 +223,7 @@ :buffer (repl-buffer) :loading-message "Evaluating..."))) (request-listener-eval - *connection* + (current-connection) string (lambda (value) (declare (ignore value)) @@ -270,16 +271,44 @@ (insert-string (current-point) string) (lem/listener-mode:listener-return))) -(define-command start-lisp-repl (&optional (use-this-window nil)) (:universal-nil) - (check-connection) +(defparameter *welcome-text* + ";; Welcome to the REPL! +;; +;; The current REPL is running in the same process as the editor. +;; If you don't need to hack the editor, +;; please start a new process with `M-x slime`. + +") + +(defun insert-repl-header (buffer self-connection) + (when self-connection + (insert-string (buffer-point buffer) + *welcome-text* + :sticky-attribute 'syntax-comment-attribute) + (update-repl-buffer-write-point (buffer-end-point buffer)))) + +(defun make-repl-buffer (self-connection) + (or (get-buffer "*lisp-repl*") + (let ((buffer (make-buffer "*lisp-repl*"))) + (insert-repl-header buffer self-connection) + buffer))) + +(defun start-lisp-repl-internal (&key use-this-window new-process) (flet ((switch (buffer split-window-p) (if split-window-p (switch-to-window (pop-to-buffer buffer)) (switch-to-buffer buffer)))) - (lem/listener-mode:listener-start - "*lisp-repl*" - 'lisp-repl-mode - :switch-to-buffer-function (alexandria:rcurry #'switch (not use-this-window))))) + (let ((buffer (make-repl-buffer (if new-process + nil + (self-connected-p))))) + (lem/listener-mode:listener-start + buffer + 'lisp-repl-mode + :switch-to-buffer-function (alexandria:rcurry #'switch (not use-this-window)))))) + +(define-command start-lisp-repl (&optional (use-this-window nil)) (:universal-nil) + (check-connection) + (start-lisp-repl-internal :use-this-window use-this-window)) (define-command lisp-switch-to-repl-buffer () () (let ((buffer (repl-buffer))) @@ -307,6 +336,10 @@ (let ((point (copy-point (buffer-point buffer) :left-inserting))) (buffer-start point))))) +(defun update-repl-buffer-write-point (point) + (move-point (repl-buffer-write-point (point-buffer point)) + point)) + (defun see-repl-writing (buffer) (when (end-buffer-p (buffer-point buffer)) (dolist (window (get-buffer-windows buffer)) @@ -315,24 +348,27 @@ (defun call-with-repl-point (function) (let* ((buffer (ensure-repl-buffer-exist)) (point (repl-buffer-write-point buffer))) - (cond (*repl-evaluating* - (buffer-end point)) - (t - (when (point<= (lem/listener-mode:input-start-point buffer) point) - (move-point point (lem/listener-mode:input-start-point buffer)) - (previous-single-property-change point :field)))) - (unless (eq (current-buffer) buffer) - (see-repl-writing buffer)) - (with-buffer-read-only buffer nil - (let ((*inhibit-read-only* t)) - (funcall function point))))) + (when (lem/listener-mode:input-start-point buffer) + (cond (*repl-evaluating* + (buffer-end point)) + (t + (when (point<= (lem/listener-mode:input-start-point buffer) point) + (move-point point (lem/listener-mode:input-start-point buffer)) + (previous-single-property-change point :field)))) + (unless (eq (current-buffer) buffer) + (see-repl-writing buffer)) + (with-buffer-read-only buffer nil + (let ((*inhibit-read-only* t)) + (funcall function point)))))) (defmacro with-repl-point ((point) &body body) `(call-with-repl-point (lambda (,point) ,@body))) -(defun write-string-to-repl (string) +(defun write-string-to-repl (string &key attribute) (with-repl-point (point) - (insert-escape-sequence-string point string) + (if attribute + (insert-string point string attribute) + (insert-escape-sequence-string point string)) (when (text-property-at point :field) (insert-character point #\newline) (character-offset point -1)))) @@ -517,7 +553,7 @@ (defun ,name ,lambda-list ,@body)))) (define-repl-shortcut sayonara () - (if (self-connection-p *connection*) + (if (self-connection-p (current-connection)) (message "Can't say sayonara because it's self connection.") (interactive-eval "(micros:quit-lisp)"))) diff --git a/extensions/lisp-mode/rpc.lisp b/extensions/lisp-mode/rpc.lisp new file mode 100644 index 000000000..c9bd4e62a --- /dev/null +++ b/extensions/lisp-mode/rpc.lisp @@ -0,0 +1,42 @@ +(uiop:define-package :lem-lisp-mode/rpc + (:use :cl) + (:import-from :lem-lisp-mode/errors + :disconnected) + (:export :write-message-to-stream + :read-message-from-stream)) +(in-package :lem-lisp-mode/rpc) + +;;; Encoding and decoding messages + +(defun encode-integer (integer) + "Encode an integer to a 0-padded 16-bit hexadecimal string." + (babel:string-to-octets (format nil "~6,'0,X" integer))) + +(defun decode-integer (string) + "Decode a string representing a 0-padded 16-bit hex string to an integer." + (parse-integer string :radix 16)) + +;; Writing and reading messages to/from streams + +(defun write-message-to-stream (stream message) + "Write a string to a stream, prefixing it with length information for Swank." + (let* ((octets (babel:string-to-octets message)) + (length-octets (encode-integer (length octets))) + (msg (make-array (+ (length length-octets) + (length octets)) + :element-type '(unsigned-byte 8)))) + (replace msg length-octets) + (replace msg octets :start1 (length length-octets)) + (write-sequence msg stream))) + +(defun read-message-from-stream (stream) + "Read a string from a string. + +Parses length information to determine how many characters to read." + (let ((length-buffer (make-array 6 :element-type '(unsigned-byte 8)))) + (when (/= 6 (read-sequence length-buffer stream)) + (error 'disconnected)) + (let* ((length (decode-integer (babel:octets-to-string length-buffer))) + (buffer (make-array length :element-type '(unsigned-byte 8)))) + (read-sequence buffer stream) + (babel:octets-to-string buffer)))) diff --git a/extensions/lisp-mode/v2/lsp-config.lisp b/extensions/lisp-mode/v2/lsp-config.lisp index b3ad86902..ab10ccf82 100644 --- a/extensions/lisp-mode/v2/lsp-config.lisp +++ b/extensions/lisp-mode/v2/lsp-config.lisp @@ -37,7 +37,7 @@ (let ((connection (lem-lisp-mode:self-connection))) (setf (connection-workspace connection) workspace)) (let* ((swank-port (gethash "swankPort" (lem-lsp-mode::workspace-server-info workspace))) - (connection (lem-lisp-mode:connect-to-swank "127.0.0.1" swank-port))) + (connection (lem-lisp-mode:connect-to-micros "127.0.0.1" swank-port))) (setf (connection-workspace connection) workspace)))) (defun start-micros-server (port) @@ -48,14 +48,14 @@ (micros:create-server :port port))) (defun start-language-server (port) - (bt:make-thread (lambda () + (bt2:make-thread (lambda () (lem-language-server:start-tcp-server port)))) (defmethod lem-lsp-mode::run-server ((spec lisp-spec)) (if (not *self-connection*) (call-next-method) - (let* ((lsp-port (lem-socket-utils:random-available-port)) - (micros-port (lem-socket-utils:random-available-port lsp-port))) + (let* ((lsp-port (lem/common/socket:random-available-port)) + (micros-port (lem/common/socket:random-available-port lsp-port))) (start-language-server lsp-port) (start-micros-server micros-port) (make-instance 'lem-lsp-mode/client:tcp-client :port lsp-port)))) diff --git a/lib/lisp-syntax/defstruct-to-defclass.lisp b/extensions/lisp-syntax/defstruct-to-defclass.lisp similarity index 99% rename from lib/lisp-syntax/defstruct-to-defclass.lisp rename to extensions/lisp-syntax/defstruct-to-defclass.lisp index c7d691742..3bd97e1c5 100644 --- a/lib/lisp-syntax/defstruct-to-defclass.lisp +++ b/extensions/lisp-syntax/defstruct-to-defclass.lisp @@ -270,7 +270,7 @@ (let ((n (skip-chars-backward point #'syntax-space-char-p))) (delete-character point n))) -(defun just-one-space (point) +(defun just-one-space* (point) (assert (eq (point-kind point) :left-inserting)) (delete-forward-whitespaces point) (insert-character point #\space) @@ -334,7 +334,7 @@ (t (insert-character point #\newline) (emit-initarg slot-info) - (just-one-space point) + (just-one-space* point) (emit-initform t))) (delete-forward-whitespaces point) (insert-character point #\newline) diff --git a/lib/lisp-syntax/enclosing.lisp b/extensions/lisp-syntax/enclosing.lisp similarity index 100% rename from lib/lisp-syntax/enclosing.lisp rename to extensions/lisp-syntax/enclosing.lisp diff --git a/lib/lisp-syntax/indent.lisp b/extensions/lisp-syntax/indent.lisp similarity index 100% rename from lib/lisp-syntax/indent.lisp rename to extensions/lisp-syntax/indent.lisp diff --git a/lib/lisp-syntax/lem-lisp-syntax.asd b/extensions/lisp-syntax/lem-lisp-syntax.asd similarity index 100% rename from lib/lisp-syntax/lem-lisp-syntax.asd rename to extensions/lisp-syntax/lem-lisp-syntax.asd diff --git a/lib/lisp-syntax/lem-lisp-syntax.lisp b/extensions/lisp-syntax/lem-lisp-syntax.lisp similarity index 100% rename from lib/lisp-syntax/lem-lisp-syntax.lisp rename to extensions/lisp-syntax/lem-lisp-syntax.lisp diff --git a/lib/lisp-syntax/misc.lisp b/extensions/lisp-syntax/misc.lisp similarity index 100% rename from lib/lisp-syntax/misc.lisp rename to extensions/lisp-syntax/misc.lisp diff --git a/lib/lisp-syntax/parse-for-autodoc.lisp b/extensions/lisp-syntax/parse-for-autodoc.lisp similarity index 100% rename from lib/lisp-syntax/parse-for-autodoc.lisp rename to extensions/lisp-syntax/parse-for-autodoc.lisp diff --git a/lib/lisp-syntax/syntax-table.lisp b/extensions/lisp-syntax/syntax-table.lisp similarity index 100% rename from lib/lisp-syntax/syntax-table.lisp rename to extensions/lisp-syntax/syntax-table.lisp diff --git a/lib/lsp-base/converter.lisp b/extensions/lsp-base/converter.lisp similarity index 96% rename from lib/lsp-base/converter.lisp rename to extensions/lsp-base/converter.lisp index a94da3c3e..ef608511c 100644 --- a/lib/lsp-base/converter.lisp +++ b/extensions/lsp-base/converter.lisp @@ -13,9 +13,10 @@ (value :initarg :value) (type :initarg :type))) -(defun assert-type (value type &optional context) +(defun assert-type (value type) (unless (typep value type) - (error 'json-type-error :value value :type type :context context)) + (log:warn "type mismatch: expected: ~A actual: ~A" type value) + (error 'json-type-error :value value :type type)) (values)) (defun exist-key-p (hash-table key) @@ -126,7 +127,7 @@ (cond (expanded (convert-from-json value type)) (t - (assert-type value type) + ;; (assert-type value type) value)))))))) (defmethod convert-to-json ((object protocol-object)) diff --git a/lib/lsp-base/lem-lsp-base.asd b/extensions/lsp-base/lem-lsp-base.asd similarity index 100% rename from lib/lsp-base/lem-lsp-base.asd rename to extensions/lsp-base/lem-lsp-base.asd diff --git a/lib/lsp-base/protocol-3-17.lisp b/extensions/lsp-base/protocol-3-17.lisp similarity index 85% rename from lib/lsp-base/protocol-3-17.lisp rename to extensions/lsp-base/protocol-3-17.lisp index 3532a7b05..55fdeaa28 100644 --- a/lib/lsp-base/protocol-3-17.lisp +++ b/extensions/lsp-base/protocol-3-17.lisp @@ -1,1608 +1,1651 @@ -;;; Code generated based on 'language-server-protocol/_specifications/lsp/3.17/metaModel/metaModel.json'; DO NOT EDIT. +;;; Code generated based on '/home/user/tmp/language-server-protocol/_specifications/lsp/3.17/metaModel/metaModel.json'; DO NOT EDIT. (common-lisp:defpackage :lem-lsp-base/protocol-3-17 (:nicknames :lsp) (:use) - (:export :*version* - :semantic-token-types - :semantic-token-types-namespace - :semantic-token-types-type - :semantic-token-types-class - :semantic-token-types-enum - :semantic-token-types-interface - :semantic-token-types-struct - :semantic-token-types-type-parameter - :semantic-token-types-parameter - :semantic-token-types-variable - :semantic-token-types-property - :semantic-token-types-enum-member - :semantic-token-types-event - :semantic-token-types-function - :semantic-token-types-method - :semantic-token-types-macro - :semantic-token-types-keyword - :semantic-token-types-modifier - :semantic-token-types-comment - :semantic-token-types-string - :semantic-token-types-number - :semantic-token-types-regexp - :semantic-token-types-operator - :semantic-token-types-decorator - :semantic-token-modifiers - :semantic-token-modifiers-declaration - :semantic-token-modifiers-definition - :semantic-token-modifiers-readonly - :semantic-token-modifiers-static - :semantic-token-modifiers-deprecated - :semantic-token-modifiers-abstract - :semantic-token-modifiers-async - :semantic-token-modifiers-modification - :semantic-token-modifiers-documentation - :semantic-token-modifiers-default-library - :document-diagnostic-report-kind - :document-diagnostic-report-kind-full - :document-diagnostic-report-kind-unchanged - :error-codes - :error-codes-parse-error - :error-codes-invalid-request - :error-codes-method-not-found - :error-codes-invalid-params - :error-codes-internal-error - :error-codes-server-not-initialized - :error-codes-unknown-error-code - :lsp-error-codes - :lsp-error-codes-request-failed - :lsp-error-codes-server-cancelled - :lsp-error-codes-content-modified - :lsp-error-codes-request-cancelled - :folding-range-kind - :folding-range-kind-comment - :folding-range-kind-imports - :folding-range-kind-region - :symbol-kind - :symbol-kind-file - :symbol-kind-module - :symbol-kind-namespace - :symbol-kind-package - :symbol-kind-class - :symbol-kind-method - :symbol-kind-property - :symbol-kind-field - :symbol-kind-constructor - :symbol-kind-enum - :symbol-kind-interface - :symbol-kind-function - :symbol-kind-variable - :symbol-kind-constant - :symbol-kind-string - :symbol-kind-number - :symbol-kind-boolean - :symbol-kind-array - :symbol-kind-object - :symbol-kind-key - :symbol-kind-null - :symbol-kind-enum-member - :symbol-kind-struct - :symbol-kind-event - :symbol-kind-operator - :symbol-kind-type-parameter - :symbol-tag - :symbol-tag-deprecated - :uniqueness-level - :uniqueness-level-document - :uniqueness-level-project - :uniqueness-level-group - :uniqueness-level-scheme - :uniqueness-level-global - :moniker-kind - :moniker-kind-import - :moniker-kind-export - :moniker-kind-local - :inlay-hint-kind - :inlay-hint-kind-type - :inlay-hint-kind-parameter - :message-type - :message-type-error - :message-type-warning - :message-type-info - :message-type-log - :text-document-sync-kind - :text-document-sync-kind-none - :text-document-sync-kind-full - :text-document-sync-kind-incremental - :text-document-save-reason - :text-document-save-reason-manual - :text-document-save-reason-after-delay - :text-document-save-reason-focus-out - :completion-item-kind - :completion-item-kind-text - :completion-item-kind-method - :completion-item-kind-function - :completion-item-kind-constructor - :completion-item-kind-field - :completion-item-kind-variable - :completion-item-kind-class - :completion-item-kind-interface - :completion-item-kind-module - :completion-item-kind-property - :completion-item-kind-unit - :completion-item-kind-value - :completion-item-kind-enum - :completion-item-kind-keyword - :completion-item-kind-snippet - :completion-item-kind-color - :completion-item-kind-file - :completion-item-kind-reference - :completion-item-kind-folder - :completion-item-kind-enum-member - :completion-item-kind-constant - :completion-item-kind-struct - :completion-item-kind-event - :completion-item-kind-operator - :completion-item-kind-type-parameter - :completion-item-tag - :completion-item-tag-deprecated - :insert-text-format - :insert-text-format-plain-text - :insert-text-format-snippet - :insert-text-mode - :insert-text-mode-as-is - :insert-text-mode-adjust-indentation - :document-highlight-kind - :document-highlight-kind-text - :document-highlight-kind-read - :document-highlight-kind-write - :code-action-kind - :code-action-kind-empty - :code-action-kind-quick-fix - :code-action-kind-refactor - :code-action-kind-refactor-extract - :code-action-kind-refactor-inline - :code-action-kind-refactor-rewrite - :code-action-kind-source - :code-action-kind-source-organize-imports - :code-action-kind-source-fix-all - :trace-values - :trace-values-off - :trace-values-messages - :trace-values-verbose - :markup-kind - :markup-kind-plain-text - :markup-kind-markdown - :position-encoding-kind - :position-encoding-kind-utf8 - :position-encoding-kind-utf16 - :position-encoding-kind-utf32 - :file-change-type - :file-change-type-created - :file-change-type-changed - :file-change-type-deleted - :watch-kind - :watch-kind-create - :watch-kind-change - :watch-kind-delete - :diagnostic-severity - :diagnostic-severity-error - :diagnostic-severity-warning - :diagnostic-severity-information - :diagnostic-severity-hint - :diagnostic-tag - :diagnostic-tag-unnecessary - :diagnostic-tag-deprecated - :completion-trigger-kind - :completion-trigger-kind-invoked - :completion-trigger-kind-trigger-character - :completion-trigger-kind-trigger-for-incomplete-completions - :signature-help-trigger-kind - :signature-help-trigger-kind-invoked - :signature-help-trigger-kind-trigger-character - :signature-help-trigger-kind-content-change - :code-action-trigger-kind - :code-action-trigger-kind-invoked - :code-action-trigger-kind-automatic - :file-operation-pattern-kind - :file-operation-pattern-kind-file - :file-operation-pattern-kind-folder - :notebook-cell-kind - :notebook-cell-kind-markup - :notebook-cell-kind-code - :resource-operation-kind - :resource-operation-kind-create - :resource-operation-kind-rename - :resource-operation-kind-delete - :failure-handling-kind - :failure-handling-kind-abort - :failure-handling-kind-transactional - :failure-handling-kind-text-only-transactional - :failure-handling-kind-undo - :prepare-support-default-behavior - :prepare-support-default-behavior-identifier - :token-format - :token-format-relative - :implementation-params - :location-uri - :location-range - :location - :uri - :range - :implementation-registration-options - :type-definition-params - :type-definition-registration-options - :workspace-folder-uri - :workspace-folder-name - :workspace-folder - :name - :did-change-workspace-folders-params-event - :did-change-workspace-folders-params - :event - :configuration-params-items - :configuration-params - :items - :document-color-params-text-document - :document-color-params - :text-document - :color-information-range - :color-information-color - :color-information - :color - :document-color-registration-options - :color-presentation-params-text-document - :color-presentation-params-color - :color-presentation-params-range - :color-presentation-params - :color-presentation-label - :color-presentation-text-edit - :color-presentation-additional-text-edits - :color-presentation - :label - :text-edit - :additional-text-edits - :work-done-progress-options-work-done-progress - :work-done-progress-options - :work-done-progress - :text-document-registration-options-document-selector - :text-document-registration-options - :document-selector - :folding-range-params-text-document - :folding-range-params - :folding-range-start-line - :folding-range-start-character - :folding-range-end-line - :folding-range-end-character - :folding-range-collapsed-text - :folding-range - :start-line - :start-character - :end-line - :end-character - :kind - :collapsed-text - :folding-range-registration-options - :declaration-params - :declaration-registration-options - :selection-range-params-text-document - :selection-range-params-positions - :selection-range-params - :positions - :selection-range-range - :selection-range-parent - :selection-range - :parent - :selection-range-registration-options - :work-done-progress-create-params-token - :work-done-progress-create-params - :token - :work-done-progress-cancel-params-token - :work-done-progress-cancel-params - :call-hierarchy-prepare-params - :call-hierarchy-item-name - :call-hierarchy-item-kind - :call-hierarchy-item-tags - :call-hierarchy-item-detail - :call-hierarchy-item-uri - :call-hierarchy-item-range - :call-hierarchy-item-selection-range - :call-hierarchy-item-data - :call-hierarchy-item - :tags - :detail - :data - :call-hierarchy-registration-options - :call-hierarchy-incoming-calls-params-item - :call-hierarchy-incoming-calls-params - :item - :call-hierarchy-incoming-call-from - :call-hierarchy-incoming-call-from-ranges - :call-hierarchy-incoming-call - :from - :from-ranges - :call-hierarchy-outgoing-calls-params-item - :call-hierarchy-outgoing-calls-params - :call-hierarchy-outgoing-call-to - :call-hierarchy-outgoing-call-from-ranges - :call-hierarchy-outgoing-call - :to - :semantic-tokens-params-text-document - :semantic-tokens-params - :semantic-tokens-result-id - :semantic-tokens-data - :semantic-tokens - :result-id - :semantic-tokens-partial-result-data - :semantic-tokens-partial-result - :semantic-tokens-registration-options - :semantic-tokens-delta-params-text-document - :semantic-tokens-delta-params-previous-result-id - :semantic-tokens-delta-params - :previous-result-id - :semantic-tokens-delta-result-id - :semantic-tokens-delta-edits - :semantic-tokens-delta - :edits - :semantic-tokens-delta-partial-result-edits - :semantic-tokens-delta-partial-result - :semantic-tokens-range-params-text-document - :semantic-tokens-range-params-range - :semantic-tokens-range-params - :show-document-params-uri - :show-document-params-external - :show-document-params-take-focus - :show-document-params-selection - :show-document-params - :external - :take-focus - :selection - :show-document-result-success - :show-document-result - :success - :linked-editing-range-params - :linked-editing-ranges-ranges - :linked-editing-ranges-word-pattern - :linked-editing-ranges - :ranges - :word-pattern - :linked-editing-range-registration-options - :create-files-params-files - :create-files-params - :files - :workspace-edit-changes - :workspace-edit-document-changes - :workspace-edit-change-annotations - :workspace-edit - :changes - :document-changes - :change-annotations - :file-operation-registration-options-filters - :file-operation-registration-options - :filters - :rename-files-params-files - :rename-files-params - :delete-files-params-files - :delete-files-params - :moniker-params - :moniker-scheme - :moniker-identifier - :moniker-unique - :moniker - :scheme - :identifier - :unique - :moniker-registration-options - :type-hierarchy-prepare-params - :type-hierarchy-item-name - :type-hierarchy-item-kind - :type-hierarchy-item-tags - :type-hierarchy-item-detail - :type-hierarchy-item-uri - :type-hierarchy-item-range - :type-hierarchy-item-selection-range - :type-hierarchy-item-data - :type-hierarchy-item - :type-hierarchy-registration-options - :type-hierarchy-supertypes-params-item - :type-hierarchy-supertypes-params - :type-hierarchy-subtypes-params-item - :type-hierarchy-subtypes-params - :inline-value-params-text-document - :inline-value-params-range - :inline-value-params-context - :inline-value-params - :context - :inline-value-registration-options - :inlay-hint-params-text-document - :inlay-hint-params-range - :inlay-hint-params - :inlay-hint-position - :inlay-hint-label - :inlay-hint-text-edits - :inlay-hint-tooltip - :inlay-hint-padding-left - :inlay-hint-padding-right - :inlay-hint-data - :inlay-hint - :position - :text-edits - :tooltip - :padding-left - :padding-right - :inlay-hint-registration-options - :document-diagnostic-params-text-document - :document-diagnostic-params-identifier - :document-diagnostic-params-previous-result-id - :document-diagnostic-params - :document-diagnostic-report-partial-result-related-documents - :document-diagnostic-report-partial-result - :related-documents - :diagnostic-server-cancellation-data-retrigger-request - :diagnostic-server-cancellation-data - :retrigger-request - :diagnostic-registration-options - :workspace-diagnostic-params-identifier - :workspace-diagnostic-params-previous-result-ids - :workspace-diagnostic-params - :previous-result-ids - :workspace-diagnostic-report-items - :workspace-diagnostic-report - :workspace-diagnostic-report-partial-result-items - :workspace-diagnostic-report-partial-result - :did-open-notebook-document-params-notebook-document - :did-open-notebook-document-params-cell-text-documents - :did-open-notebook-document-params - :notebook-document - :cell-text-documents - :did-change-notebook-document-params-notebook-document - :did-change-notebook-document-params-change - :did-change-notebook-document-params - :change - :did-save-notebook-document-params-notebook-document - :did-save-notebook-document-params - :did-close-notebook-document-params-notebook-document - :did-close-notebook-document-params-cell-text-documents - :did-close-notebook-document-params - :registration-params-registrations - :registration-params - :registrations - :unregistration-params-unregisterations - :unregistration-params - :unregisterations - :initialize-params - :initialize-result-capabilities - :initialize-result-server-info - :initialize-result - :capabilities - :server-info - :initialize-error-retry - :initialize-error - :retry - :initialized-params - :did-change-configuration-params-settings - :did-change-configuration-params - :settings - :did-change-configuration-registration-options-section - :did-change-configuration-registration-options - :section - :show-message-params-type - :show-message-params-message - :show-message-params - :type - :message - :show-message-request-params-type - :show-message-request-params-message - :show-message-request-params-actions - :show-message-request-params - :actions - :message-action-item-title - :message-action-item - :title - :log-message-params-type - :log-message-params-message - :log-message-params - :did-open-text-document-params-text-document - :did-open-text-document-params - :did-change-text-document-params-text-document - :did-change-text-document-params-content-changes - :did-change-text-document-params - :content-changes - :text-document-change-registration-options-sync-kind - :text-document-change-registration-options - :sync-kind - :did-close-text-document-params-text-document - :did-close-text-document-params - :did-save-text-document-params-text-document - :did-save-text-document-params-text - :did-save-text-document-params - :text - :text-document-save-registration-options - :will-save-text-document-params-text-document - :will-save-text-document-params-reason - :will-save-text-document-params - :reason - :text-edit-range - :text-edit-new-text - :new-text - :did-change-watched-files-params-changes - :did-change-watched-files-params - :did-change-watched-files-registration-options-watchers - :did-change-watched-files-registration-options - :watchers - :publish-diagnostics-params-uri - :publish-diagnostics-params-version - :publish-diagnostics-params-diagnostics - :publish-diagnostics-params - :version - :diagnostics - :completion-params-context - :completion-params - :completion-item-label - :completion-item-label-details - :completion-item-tags - :completion-item-detail - :completion-item-documentation - :completion-item-deprecated - :completion-item-preselect - :completion-item-sort-text - :completion-item-filter-text - :completion-item-insert-text - :completion-item-insert-text-format - :completion-item-insert-text-mode - :completion-item-text-edit - :completion-item-text-edit-text - :completion-item-additional-text-edits - :completion-item-commit-characters - :completion-item-command - :completion-item-data - :completion-item - :label-details - :documentation - :deprecated - :preselect - :sort-text - :filter-text - :insert-text - :text-edit-text - :commit-characters - :command - :completion-list-is-incomplete - :completion-list-item-defaults - :completion-list-items - :completion-list - :is-incomplete - :item-defaults - :completion-registration-options - :hover-params - :hover-contents - :hover-range - :hover - :contents - :hover-registration-options - :signature-help-params-context - :signature-help-params - :signature-help-signatures - :signature-help-active-signature - :signature-help-active-parameter - :signature-help - :signatures - :active-signature - :active-parameter - :signature-help-registration-options - :definition-params - :definition-registration-options - :reference-params-context - :reference-params - :reference-registration-options - :document-highlight-params - :document-highlight-range - :document-highlight - :document-highlight-registration-options - :document-symbol-params-text-document - :document-symbol-params - :symbol-information-deprecated - :symbol-information-location - :symbol-information - :document-symbol-name - :document-symbol-detail - :document-symbol-kind - :document-symbol-tags - :document-symbol-deprecated - :document-symbol-range - :document-symbol-selection-range - :document-symbol-children - :document-symbol - :children - :document-symbol-registration-options - :code-action-params-text-document - :code-action-params-range - :code-action-params-context - :code-action-params - :command-title - :command-command - :command-arguments - :arguments - :code-action-title - :code-action-diagnostics - :code-action-is-preferred - :code-action-disabled - :code-action-edit - :code-action-command - :code-action-data - :code-action - :is-preferred - :disabled - :edit - :code-action-registration-options - :workspace-symbol-params-query - :workspace-symbol-params - :query - :workspace-symbol-location - :workspace-symbol-data - :workspace-symbol - :workspace-symbol-registration-options - :code-lens-params-text-document - :code-lens-params - :code-lens-range - :code-lens-command - :code-lens-data - :code-lens - :code-lens-registration-options - :document-link-params-text-document - :document-link-params - :document-link-range - :document-link-target - :document-link-tooltip - :document-link-data - :document-link - :target - :document-link-registration-options - :document-formatting-params-text-document - :document-formatting-params-options - :document-formatting-params - :options - :document-formatting-registration-options - :document-range-formatting-params-text-document - :document-range-formatting-params-range - :document-range-formatting-params-options - :document-range-formatting-params - :document-range-formatting-registration-options - :document-on-type-formatting-params-text-document - :document-on-type-formatting-params-position - :document-on-type-formatting-params-ch - :document-on-type-formatting-params-options - :document-on-type-formatting-params - :ch - :document-on-type-formatting-registration-options - :rename-params-text-document - :rename-params-position - :rename-params-new-name - :rename-params - :new-name - :rename-registration-options - :prepare-rename-params - :execute-command-params-command - :execute-command-params-arguments - :execute-command-params - :execute-command-registration-options - :apply-workspace-edit-params-label - :apply-workspace-edit-params-edit - :apply-workspace-edit-params - :apply-workspace-edit-result-applied - :apply-workspace-edit-result-failure-reason - :apply-workspace-edit-result-failed-change - :apply-workspace-edit-result - :applied - :failure-reason - :failed-change - :work-done-progress-begin-kind - :work-done-progress-begin-title - :work-done-progress-begin-cancellable - :work-done-progress-begin-message - :work-done-progress-begin-percentage - :work-done-progress-begin - :cancellable - :percentage - :work-done-progress-report-kind - :work-done-progress-report-cancellable - :work-done-progress-report-message - :work-done-progress-report-percentage - :work-done-progress-report - :work-done-progress-end-kind - :work-done-progress-end-message - :work-done-progress-end - :set-trace-params-value - :set-trace-params - :value - :log-trace-params-message - :log-trace-params-verbose - :log-trace-params - :verbose - :cancel-params-id - :cancel-params - :id - :progress-params-token - :progress-params-value - :progress-params - :text-document-position-params-text-document - :text-document-position-params-position - :text-document-position-params - :work-done-progress-params-work-done-token - :work-done-progress-params - :work-done-token - :partial-result-params-partial-result-token - :partial-result-params - :partial-result-token - :location-link-origin-selection-range - :location-link-target-uri - :location-link-target-range - :location-link-target-selection-range - :location-link - :origin-selection-range - :target-uri - :target-range - :target-selection-range - :range-start - :range-end - :start - :end - :implementation-options - :static-registration-options-id - :static-registration-options - :type-definition-options - :workspace-folders-change-event-added - :workspace-folders-change-event-removed - :workspace-folders-change-event - :added - :removed - :configuration-item-scope-uri - :configuration-item-section - :configuration-item - :scope-uri - :text-document-identifier-uri - :text-document-identifier - :color-red - :color-green - :color-blue - :color-alpha - :red - :green - :blue - :alpha - :document-color-options - :folding-range-options - :declaration-options - :position-line - :position-character - :line - :character - :selection-range-options - :call-hierarchy-options - :semantic-tokens-options-legend - :semantic-tokens-options-range - :semantic-tokens-options-full - :semantic-tokens-options - :legend - :full - :semantic-tokens-edit-start - :semantic-tokens-edit-delete-count - :semantic-tokens-edit-data - :semantic-tokens-edit - :delete-count - :linked-editing-range-options - :file-create-uri - :file-create - :text-document-edit-text-document - :text-document-edit-edits - :text-document-edit - :create-file-kind - :create-file-uri - :create-file-options - :create-file - :rename-file-kind - :rename-file-old-uri - :rename-file-new-uri - :rename-file-options - :rename-file - :old-uri - :new-uri - :delete-file-kind - :delete-file-uri - :delete-file-options - :delete-file - :change-annotation-label - :change-annotation-needs-confirmation - :change-annotation-description - :change-annotation - :needs-confirmation - :description - :file-operation-filter-scheme - :file-operation-filter-pattern - :file-operation-filter - :pattern - :file-rename-old-uri - :file-rename-new-uri - :file-rename - :file-delete-uri - :file-delete - :moniker-options - :type-hierarchy-options - :inline-value-context-frame-id - :inline-value-context-stopped-location - :inline-value-context - :frame-id - :stopped-location - :inline-value-text-range - :inline-value-text-text - :inline-value-text - :inline-value-variable-lookup-range - :inline-value-variable-lookup-variable-name - :inline-value-variable-lookup-case-sensitive-lookup - :inline-value-variable-lookup - :variable-name - :case-sensitive-lookup - :inline-value-evaluatable-expression-range - :inline-value-evaluatable-expression-expression - :inline-value-evaluatable-expression - :expression - :inline-value-options - :inlay-hint-label-part-value - :inlay-hint-label-part-tooltip - :inlay-hint-label-part-location - :inlay-hint-label-part-command - :inlay-hint-label-part - :markup-content-kind - :markup-content-value - :markup-content - :inlay-hint-options-resolve-provider - :inlay-hint-options - :resolve-provider - :related-full-document-diagnostic-report-related-documents - :related-full-document-diagnostic-report - :related-unchanged-document-diagnostic-report-related-documents - :related-unchanged-document-diagnostic-report - :full-document-diagnostic-report-kind - :full-document-diagnostic-report-result-id - :full-document-diagnostic-report-items - :full-document-diagnostic-report - :unchanged-document-diagnostic-report-kind - :unchanged-document-diagnostic-report-result-id - :unchanged-document-diagnostic-report - :diagnostic-options-identifier - :diagnostic-options-inter-file-dependencies - :diagnostic-options-workspace-diagnostics - :diagnostic-options - :inter-file-dependencies - :workspace-diagnostics - :previous-result-id-uri - :previous-result-id-value - :notebook-document-uri - :notebook-document-notebook-type - :notebook-document-version - :notebook-document-metadata - :notebook-document-cells - :notebook-type - :metadata - :cells - :text-document-item-uri - :text-document-item-language-id - :text-document-item-version - :text-document-item-text - :text-document-item - :language-id - :versioned-notebook-document-identifier-version - :versioned-notebook-document-identifier-uri - :versioned-notebook-document-identifier - :notebook-document-change-event-metadata - :notebook-document-change-event-cells - :notebook-document-change-event - :notebook-document-identifier-uri - :notebook-document-identifier - :registration-id - :registration-method - :registration-register-options - :registration - :method - :register-options - :unregistration-id - :unregistration-method - :unregistration - :_initialize-params-process-id - :_initialize-params-client-info - :_initialize-params-locale - :_initialize-params-root-path - :_initialize-params-root-uri - :_initialize-params-capabilities - :_initialize-params-initialization-options - :_initialize-params-trace - :_initialize-params - :process-id - :client-info - :locale - :root-path - :root-uri - :initialization-options - :trace - :workspace-folders-initialize-params-workspace-folders - :workspace-folders-initialize-params - :workspace-folders - :server-capabilities-position-encoding - :server-capabilities-text-document-sync - :server-capabilities-notebook-document-sync - :server-capabilities-completion-provider - :server-capabilities-hover-provider - :server-capabilities-signature-help-provider - :server-capabilities-declaration-provider - :server-capabilities-definition-provider - :server-capabilities-type-definition-provider - :server-capabilities-implementation-provider - :server-capabilities-references-provider - :server-capabilities-document-highlight-provider - :server-capabilities-document-symbol-provider - :server-capabilities-code-action-provider - :server-capabilities-code-lens-provider - :server-capabilities-document-link-provider - :server-capabilities-color-provider - :server-capabilities-workspace-symbol-provider - :server-capabilities-document-formatting-provider - :server-capabilities-document-range-formatting-provider - :server-capabilities-document-on-type-formatting-provider - :server-capabilities-rename-provider - :server-capabilities-folding-range-provider - :server-capabilities-selection-range-provider - :server-capabilities-execute-command-provider - :server-capabilities-call-hierarchy-provider - :server-capabilities-linked-editing-range-provider - :server-capabilities-semantic-tokens-provider - :server-capabilities-moniker-provider - :server-capabilities-type-hierarchy-provider - :server-capabilities-inline-value-provider - :server-capabilities-inlay-hint-provider - :server-capabilities-diagnostic-provider - :server-capabilities-workspace - :server-capabilities-experimental - :server-capabilities - :position-encoding - :text-document-sync - :notebook-document-sync - :completion-provider - :hover-provider - :signature-help-provider - :declaration-provider - :definition-provider - :type-definition-provider - :implementation-provider - :references-provider - :document-highlight-provider - :document-symbol-provider - :code-action-provider - :code-lens-provider - :document-link-provider - :color-provider - :workspace-symbol-provider - :document-formatting-provider - :document-range-formatting-provider - :document-on-type-formatting-provider - :rename-provider - :folding-range-provider - :selection-range-provider - :execute-command-provider - :call-hierarchy-provider - :linked-editing-range-provider - :semantic-tokens-provider - :moniker-provider - :type-hierarchy-provider - :inline-value-provider - :inlay-hint-provider - :diagnostic-provider - :workspace - :experimental - :versioned-text-document-identifier-version - :versioned-text-document-identifier - :save-options-include-text - :save-options - :include-text - :file-event-uri - :file-event-type - :file-event - :file-system-watcher-glob-pattern - :file-system-watcher-kind - :file-system-watcher - :glob-pattern - :diagnostic-range - :diagnostic-code - :diagnostic-code-description - :diagnostic-source - :diagnostic-message - :diagnostic-tags - :diagnostic-related-information - :diagnostic-data - :diagnostic - :severity - :code - :code-description - :source - :related-information - :completion-context-trigger-kind - :completion-context-trigger-character - :completion-context - :trigger-kind - :trigger-character - :completion-item-label-details-detail - :completion-item-label-details-description - :insert-replace-edit-new-text - :insert-replace-edit-insert - :insert-replace-edit-replace - :insert-replace-edit - :insert - :replace - :completion-options-trigger-characters - :completion-options-all-commit-characters - :completion-options-resolve-provider - :completion-options-completion-item - :completion-options - :trigger-characters - :all-commit-characters - :hover-options - :signature-help-context-trigger-kind - :signature-help-context-trigger-character - :signature-help-context-is-retrigger - :signature-help-context-active-signature-help - :signature-help-context - :is-retrigger - :active-signature-help - :signature-information-label - :signature-information-documentation - :signature-information-parameters - :signature-information-active-parameter - :signature-information - :parameters - :signature-help-options-trigger-characters - :signature-help-options-retrigger-characters - :signature-help-options - :retrigger-characters - :definition-options - :reference-context-include-declaration - :reference-context - :include-declaration - :reference-options - :document-highlight-options - :base-symbol-information-name - :base-symbol-information-kind - :base-symbol-information-tags - :base-symbol-information-container-name - :base-symbol-information - :container-name - :document-symbol-options-label - :document-symbol-options - :code-action-context-diagnostics - :code-action-context-only - :code-action-context-trigger-kind - :code-action-context - :only - :code-action-options-code-action-kinds - :code-action-options-resolve-provider - :code-action-options - :code-action-kinds - :workspace-symbol-options-resolve-provider - :workspace-symbol-options - :code-lens-options-resolve-provider - :code-lens-options - :document-link-options-resolve-provider - :document-link-options - :formatting-options-tab-size - :formatting-options-insert-spaces - :formatting-options-trim-trailing-whitespace - :formatting-options-insert-final-newline - :formatting-options-trim-final-newlines - :formatting-options - :tab-size - :insert-spaces - :trim-trailing-whitespace - :insert-final-newline - :trim-final-newlines - :document-formatting-options - :document-range-formatting-options - :document-on-type-formatting-options-first-trigger-character - :document-on-type-formatting-options-more-trigger-character - :document-on-type-formatting-options - :first-trigger-character - :more-trigger-character - :rename-options-prepare-provider - :rename-options - :prepare-provider - :execute-command-options-commands - :execute-command-options - :commands - :semantic-tokens-legend-token-types - :semantic-tokens-legend-token-modifiers - :semantic-tokens-legend - :token-types - :token-modifiers - :optional-versioned-text-document-identifier-version - :optional-versioned-text-document-identifier - :annotated-text-edit-annotation-id - :annotated-text-edit - :annotation-id - :resource-operation-annotation-id - :resource-operation - :create-file-options-overwrite - :create-file-options-ignore-if-exists - :overwrite - :ignore-if-exists - :rename-file-options-overwrite - :rename-file-options-ignore-if-exists - :delete-file-options-recursive - :delete-file-options-ignore-if-not-exists - :recursive - :ignore-if-not-exists - :file-operation-pattern-glob - :file-operation-pattern-matches - :file-operation-pattern-options - :file-operation-pattern - :glob - :matches - :workspace-full-document-diagnostic-report-uri - :workspace-full-document-diagnostic-report-version - :workspace-full-document-diagnostic-report - :workspace-unchanged-document-diagnostic-report-uri - :workspace-unchanged-document-diagnostic-report-version - :workspace-unchanged-document-diagnostic-report - :notebook-cell-document - :notebook-cell-metadata - :notebook-cell-execution-summary - :notebook-cell - :document - :execution-summary - :notebook-cell-array-change-start - :notebook-cell-array-change-delete-count - :notebook-cell-array-change-cells - :notebook-cell-array-change - :client-capabilities-workspace - :client-capabilities-text-document - :client-capabilities-notebook-document - :client-capabilities-window - :client-capabilities-general - :client-capabilities-experimental - :client-capabilities - :window - :general - :text-document-sync-options-open-close - :text-document-sync-options-change - :text-document-sync-options-will-save - :text-document-sync-options-will-save-wait-until - :text-document-sync-options-save - :text-document-sync-options - :open-close - :will-save - :will-save-wait-until - :save - :notebook-document-sync-options-notebook-selector - :notebook-document-sync-options-save - :notebook-document-sync-options - :notebook-selector - :notebook-document-sync-registration-options - :workspace-folders-server-capabilities-supported - :workspace-folders-server-capabilities-change-notifications - :workspace-folders-server-capabilities - :supported - :change-notifications - :file-operation-options-did-create - :file-operation-options-will-create - :file-operation-options-did-rename - :file-operation-options-will-rename - :file-operation-options-did-delete - :file-operation-options-will-delete - :file-operation-options - :did-create - :will-create - :did-rename - :will-rename - :did-delete - :will-delete - :code-description-href - :href - :diagnostic-related-information-location - :diagnostic-related-information-message - :parameter-information-label - :parameter-information-documentation - :parameter-information - :notebook-cell-text-document-filter-notebook - :notebook-cell-text-document-filter-language - :notebook-cell-text-document-filter - :notebook - :language - :file-operation-pattern-options-ignore-case - :ignore-case - :execution-summary-execution-order - :execution-summary-success - :execution-order - :workspace-client-capabilities-apply-edit - :workspace-client-capabilities-workspace-edit - :workspace-client-capabilities-did-change-configuration - :workspace-client-capabilities-did-change-watched-files - :workspace-client-capabilities-symbol - :workspace-client-capabilities-execute-command - :workspace-client-capabilities-workspace-folders - :workspace-client-capabilities-configuration - :workspace-client-capabilities-semantic-tokens - :workspace-client-capabilities-code-lens - :workspace-client-capabilities-file-operations - :workspace-client-capabilities-inline-value - :workspace-client-capabilities-inlay-hint - :workspace-client-capabilities-diagnostics - :workspace-client-capabilities - :apply-edit - :did-change-configuration - :did-change-watched-files - :symbol - :execute-command - :configuration - :file-operations - :inline-value - :text-document-client-capabilities-synchronization - :text-document-client-capabilities-completion - :text-document-client-capabilities-hover - :text-document-client-capabilities-signature-help - :text-document-client-capabilities-declaration - :text-document-client-capabilities-definition - :text-document-client-capabilities-type-definition - :text-document-client-capabilities-implementation - :text-document-client-capabilities-references - :text-document-client-capabilities-document-highlight - :text-document-client-capabilities-document-symbol - :text-document-client-capabilities-code-action - :text-document-client-capabilities-code-lens - :text-document-client-capabilities-document-link - :text-document-client-capabilities-color-provider - :text-document-client-capabilities-formatting - :text-document-client-capabilities-range-formatting - :text-document-client-capabilities-on-type-formatting - :text-document-client-capabilities-rename - :text-document-client-capabilities-folding-range - :text-document-client-capabilities-selection-range - :text-document-client-capabilities-publish-diagnostics - :text-document-client-capabilities-call-hierarchy - :text-document-client-capabilities-semantic-tokens - :text-document-client-capabilities-linked-editing-range - :text-document-client-capabilities-moniker - :text-document-client-capabilities-type-hierarchy - :text-document-client-capabilities-inline-value - :text-document-client-capabilities-inlay-hint - :text-document-client-capabilities-diagnostic - :text-document-client-capabilities - :synchronization - :completion - :declaration - :definition - :type-definition - :implementation - :references - :formatting - :range-formatting - :on-type-formatting - :rename - :publish-diagnostics - :call-hierarchy - :linked-editing-range - :type-hierarchy - :notebook-document-client-capabilities-synchronization - :notebook-document-client-capabilities - :window-client-capabilities-work-done-progress - :window-client-capabilities-show-message - :window-client-capabilities-show-document - :window-client-capabilities - :show-message - :show-document - :general-client-capabilities-stale-request-support - :general-client-capabilities-regular-expressions - :general-client-capabilities-markdown - :general-client-capabilities-position-encodings - :general-client-capabilities - :stale-request-support - :regular-expressions - :markdown - :position-encodings - :relative-pattern-base-uri - :relative-pattern-pattern - :relative-pattern - :base-uri - :workspace-edit-client-capabilities-document-changes - :workspace-edit-client-capabilities-resource-operations - :workspace-edit-client-capabilities-failure-handling - :workspace-edit-client-capabilities-normalizes-line-endings - :workspace-edit-client-capabilities-change-annotation-support - :workspace-edit-client-capabilities - :resource-operations - :failure-handling - :normalizes-line-endings - :change-annotation-support - :did-change-configuration-client-capabilities-dynamic-registration - :did-change-configuration-client-capabilities - :dynamic-registration - :did-change-watched-files-client-capabilities-dynamic-registration - :did-change-watched-files-client-capabilities-relative-pattern-support - :did-change-watched-files-client-capabilities - :relative-pattern-support - :workspace-symbol-client-capabilities-dynamic-registration - :workspace-symbol-client-capabilities-symbol-kind - :workspace-symbol-client-capabilities-tag-support - :workspace-symbol-client-capabilities-resolve-support - :workspace-symbol-client-capabilities - :tag-support - :resolve-support - :execute-command-client-capabilities-dynamic-registration - :execute-command-client-capabilities - :semantic-tokens-workspace-client-capabilities-refresh-support - :semantic-tokens-workspace-client-capabilities - :refresh-support - :code-lens-workspace-client-capabilities-refresh-support - :code-lens-workspace-client-capabilities - :file-operation-client-capabilities-dynamic-registration - :file-operation-client-capabilities-did-create - :file-operation-client-capabilities-will-create - :file-operation-client-capabilities-did-rename - :file-operation-client-capabilities-will-rename - :file-operation-client-capabilities-did-delete - :file-operation-client-capabilities-will-delete - :file-operation-client-capabilities - :inline-value-workspace-client-capabilities-refresh-support - :inline-value-workspace-client-capabilities - :inlay-hint-workspace-client-capabilities-refresh-support - :inlay-hint-workspace-client-capabilities - :diagnostic-workspace-client-capabilities-refresh-support - :diagnostic-workspace-client-capabilities - :text-document-sync-client-capabilities-dynamic-registration - :text-document-sync-client-capabilities-will-save - :text-document-sync-client-capabilities-will-save-wait-until - :text-document-sync-client-capabilities-did-save - :text-document-sync-client-capabilities - :did-save - :completion-client-capabilities-dynamic-registration - :completion-client-capabilities-completion-item - :completion-client-capabilities-completion-item-kind - :completion-client-capabilities-insert-text-mode - :completion-client-capabilities-context-support - :completion-client-capabilities-completion-list - :completion-client-capabilities - :context-support - :hover-client-capabilities-dynamic-registration - :hover-client-capabilities-content-format - :hover-client-capabilities - :content-format - :signature-help-client-capabilities-dynamic-registration - :signature-help-client-capabilities-signature-information - :signature-help-client-capabilities-context-support - :signature-help-client-capabilities - :declaration-client-capabilities-dynamic-registration - :declaration-client-capabilities-link-support - :declaration-client-capabilities - :link-support - :definition-client-capabilities-dynamic-registration - :definition-client-capabilities-link-support - :definition-client-capabilities - :type-definition-client-capabilities-dynamic-registration - :type-definition-client-capabilities-link-support - :type-definition-client-capabilities - :implementation-client-capabilities-dynamic-registration - :implementation-client-capabilities-link-support - :implementation-client-capabilities - :reference-client-capabilities-dynamic-registration - :reference-client-capabilities - :document-highlight-client-capabilities-dynamic-registration - :document-highlight-client-capabilities - :document-symbol-client-capabilities-dynamic-registration - :document-symbol-client-capabilities-symbol-kind - :document-symbol-client-capabilities-hierarchical-document-symbol-support - :document-symbol-client-capabilities-tag-support - :document-symbol-client-capabilities-label-support - :document-symbol-client-capabilities - :hierarchical-document-symbol-support - :label-support - :code-action-client-capabilities-dynamic-registration - :code-action-client-capabilities-code-action-literal-support - :code-action-client-capabilities-is-preferred-support - :code-action-client-capabilities-disabled-support - :code-action-client-capabilities-data-support - :code-action-client-capabilities-resolve-support - :code-action-client-capabilities-honors-change-annotations - :code-action-client-capabilities - :code-action-literal-support - :is-preferred-support - :disabled-support - :data-support - :honors-change-annotations - :code-lens-client-capabilities-dynamic-registration - :code-lens-client-capabilities - :document-link-client-capabilities-dynamic-registration - :document-link-client-capabilities-tooltip-support - :document-link-client-capabilities - :tooltip-support - :document-color-client-capabilities-dynamic-registration - :document-color-client-capabilities - :document-formatting-client-capabilities-dynamic-registration - :document-formatting-client-capabilities - :document-range-formatting-client-capabilities-dynamic-registration - :document-range-formatting-client-capabilities - :document-on-type-formatting-client-capabilities-dynamic-registration - :document-on-type-formatting-client-capabilities - :rename-client-capabilities-dynamic-registration - :rename-client-capabilities-prepare-support - :rename-client-capabilities-prepare-support-default-behavior - :rename-client-capabilities-honors-change-annotations - :rename-client-capabilities - :prepare-support - :folding-range-client-capabilities-dynamic-registration - :folding-range-client-capabilities-range-limit - :folding-range-client-capabilities-line-folding-only - :folding-range-client-capabilities-folding-range-kind - :folding-range-client-capabilities-folding-range - :folding-range-client-capabilities - :range-limit - :line-folding-only - :selection-range-client-capabilities-dynamic-registration - :selection-range-client-capabilities - :publish-diagnostics-client-capabilities-related-information - :publish-diagnostics-client-capabilities-tag-support - :publish-diagnostics-client-capabilities-version-support - :publish-diagnostics-client-capabilities-code-description-support - :publish-diagnostics-client-capabilities-data-support - :publish-diagnostics-client-capabilities - :version-support - :code-description-support - :call-hierarchy-client-capabilities-dynamic-registration - :call-hierarchy-client-capabilities - :semantic-tokens-client-capabilities-dynamic-registration - :semantic-tokens-client-capabilities-requests - :semantic-tokens-client-capabilities-token-types - :semantic-tokens-client-capabilities-token-modifiers - :semantic-tokens-client-capabilities-formats - :semantic-tokens-client-capabilities-overlapping-token-support - :semantic-tokens-client-capabilities-multiline-token-support - :semantic-tokens-client-capabilities-server-cancel-support - :semantic-tokens-client-capabilities-augments-syntax-tokens - :semantic-tokens-client-capabilities - :requests - :formats - :overlapping-token-support - :multiline-token-support - :server-cancel-support - :augments-syntax-tokens - :linked-editing-range-client-capabilities-dynamic-registration - :linked-editing-range-client-capabilities - :moniker-client-capabilities-dynamic-registration - :moniker-client-capabilities - :type-hierarchy-client-capabilities-dynamic-registration - :type-hierarchy-client-capabilities - :inline-value-client-capabilities-dynamic-registration - :inline-value-client-capabilities - :inlay-hint-client-capabilities-dynamic-registration - :inlay-hint-client-capabilities-resolve-support - :inlay-hint-client-capabilities - :diagnostic-client-capabilities-dynamic-registration - :diagnostic-client-capabilities-related-document-support - :diagnostic-client-capabilities - :related-document-support - :notebook-document-sync-client-capabilities-dynamic-registration - :notebook-document-sync-client-capabilities-execution-summary-support - :notebook-document-sync-client-capabilities - :execution-summary-support - :show-message-request-client-capabilities-message-action-item - :show-message-request-client-capabilities - :show-document-client-capabilities-support - :show-document-client-capabilities - :support - :regular-expressions-client-capabilities-engine - :regular-expressions-client-capabilities-version - :regular-expressions-client-capabilities - :engine - :markdown-client-capabilities-parser - :markdown-client-capabilities-version - :markdown-client-capabilities-allowed-tags - :markdown-client-capabilities - :parser - :allowed-tags - :definition-link - :lsp-array - :lsp-any - :declaration-link - :document-diagnostic-report - :prepare-rename-result - :progress-token - :change-annotation-identifier - :workspace-document-diagnostic-report - :text-document-content-change-event - :marked-string - :document-filter - :lsp-object - :text-document-filter - :notebook-document-filter - :text-document/implementation - :text-document/type-definition - :workspace/workspace-folders - :workspace/configuration - :text-document/document-color - :text-document/color-presentation - :text-document/folding-range - :text-document/declaration - :text-document/selection-range - :window/work-done-progress/create - :text-document/prepare-call-hierarchy - :call-hierarchy/incoming-calls - :call-hierarchy/outgoing-calls - :text-document/semantic-tokens/full - :text-document/semantic-tokens/full/delta - :text-document/semantic-tokens/range - :workspace/semantic-tokens/refresh - :window/show-document - :text-document/linked-editing-range - :workspace/will-create-files - :workspace/will-rename-files - :workspace/will-delete-files - :text-document/moniker - :text-document/prepare-type-hierarchy - :type-hierarchy/supertypes - :type-hierarchy/subtypes - :text-document/inline-value - :workspace/inline-value/refresh - :text-document/inlay-hint - :inlay-hint/resolve - :workspace/inlay-hint/refresh - :text-document/diagnostic - :workspace/diagnostic - :workspace/diagnostic/refresh - :client/register-capability - :client/unregister-capability - :initialize - :shutdown - :window/show-message-request - :text-document/will-save-wait-until - :text-document/completion - :completion-item/resolve - :text-document/hover - :text-document/signature-help - :text-document/definition - :text-document/references - :text-document/document-highlight - :text-document/document-symbol - :text-document/code-action - :code-action/resolve - :workspace/symbol - :workspace-symbol/resolve - :text-document/code-lens - :code-lens/resolve - :workspace/code-lens/refresh - :text-document/document-link - :document-link/resolve - :text-document/formatting - :text-document/range-formatting - :text-document/on-type-formatting - :text-document/rename - :text-document/prepare-rename - :workspace/execute-command - :workspace/apply-edit - :workspace/did-change-workspace-folders - :window/work-done-progress/cancel - :workspace/did-create-files - :workspace/did-rename-files - :workspace/did-delete-files - :notebook-document/did-open - :notebook-document/did-change - :notebook-document/did-save - :notebook-document/did-close - :initialized - :exit - :workspace/did-change-configuration - :window/show-message - :window/log-message - :telemetry/event - :text-document/did-open - :text-document/did-change - :text-document/did-close - :text-document/did-save - :text-document/will-save - :workspace/did-change-watched-files - :text-document/publish-diagnostics - :/set-trace - :/log-trace + (:export :/progress :/cancel-request - :/progress)) + :/log-trace + :/set-trace + :text-document/publish-diagnostics + :workspace/did-change-watched-files + :text-document/will-save + :text-document/did-save + :text-document/did-close + :text-document/did-change + :text-document/did-open + :telemetry/event + :window/log-message + :window/show-message + :workspace/did-change-configuration + :exit + :initialized + :notebook-document/did-close + :notebook-document/did-save + :notebook-document/did-change + :notebook-document/did-open + :workspace/did-delete-files + :workspace/did-rename-files + :workspace/did-create-files + :window/work-done-progress/cancel + :workspace/did-change-workspace-folders + :workspace/apply-edit + :workspace/execute-command + :text-document/prepare-rename + :text-document/rename + :text-document/on-type-formatting + :text-document/ranges-formatting + :text-document/range-formatting + :text-document/formatting + :document-link/resolve + :text-document/document-link + :workspace/code-lens/refresh + :code-lens/resolve + :text-document/code-lens + :workspace-symbol/resolve + :workspace/symbol + :code-action/resolve + :text-document/code-action + :text-document/document-symbol + :text-document/document-highlight + :text-document/references + :text-document/definition + :text-document/signature-help + :text-document/hover + :completion-item/resolve + :text-document/completion + :text-document/will-save-wait-until + :window/show-message-request + :shutdown + :initialize + :client/unregister-capability + :client/register-capability + :text-document/inline-completion + :workspace/diagnostic/refresh + :workspace/diagnostic + :text-document/diagnostic + :workspace/inlay-hint/refresh + :inlay-hint/resolve + :text-document/inlay-hint + :workspace/inline-value/refresh + :text-document/inline-value + :type-hierarchy/subtypes + :type-hierarchy/supertypes + :text-document/prepare-type-hierarchy + :text-document/moniker + :workspace/will-delete-files + :workspace/will-rename-files + :workspace/will-create-files + :text-document/linked-editing-range + :window/show-document + :workspace/semantic-tokens/refresh + :text-document/semantic-tokens/range + :text-document/semantic-tokens/full/delta + :text-document/semantic-tokens/full + :call-hierarchy/outgoing-calls + :call-hierarchy/incoming-calls + :text-document/prepare-call-hierarchy + :window/work-done-progress/create + :text-document/selection-range + :text-document/declaration + :workspace/folding-range/refresh + :text-document/folding-range + :text-document/color-presentation + :text-document/document-color + :workspace/configuration + :workspace/workspace-folders + :text-document/type-definition + :text-document/implementation + :notebook-document-filter + :text-document-filter + :lsp-object + :document-filter + :marked-string + :text-document-content-change-event + :workspace-document-diagnostic-report + :change-annotation-identifier + :progress-token + :prepare-rename-result + :document-diagnostic-report + :declaration-link + :lsp-any + :lsp-array + :definition-link + :allowed-tags + :parser + :markdown-client-capabilities + :markdown-client-capabilities-allowed-tags + :markdown-client-capabilities-version + :markdown-client-capabilities-parser + :engine + :regular-expressions-client-capabilities + :regular-expressions-client-capabilities-version + :regular-expressions-client-capabilities-engine + :support + :show-document-client-capabilities + :show-document-client-capabilities-support + :show-message-request-client-capabilities + :show-message-request-client-capabilities-message-action-item + :execution-summary-support + :notebook-document-sync-client-capabilities + :notebook-document-sync-client-capabilities-execution-summary-support + :notebook-document-sync-client-capabilities-dynamic-registration + :inline-completion-client-capabilities + :inline-completion-client-capabilities-dynamic-registration + :related-document-support + :diagnostic-client-capabilities + :diagnostic-client-capabilities-related-document-support + :diagnostic-client-capabilities-dynamic-registration + :inlay-hint-client-capabilities + :inlay-hint-client-capabilities-resolve-support + :inlay-hint-client-capabilities-dynamic-registration + :inline-value-client-capabilities + :inline-value-client-capabilities-dynamic-registration + :type-hierarchy-client-capabilities + :type-hierarchy-client-capabilities-dynamic-registration + :moniker-client-capabilities + :moniker-client-capabilities-dynamic-registration + :linked-editing-range-client-capabilities + :linked-editing-range-client-capabilities-dynamic-registration + :augments-syntax-tokens + :server-cancel-support + :multiline-token-support + :overlapping-token-support + :formats + :requests + :semantic-tokens-client-capabilities + :semantic-tokens-client-capabilities-augments-syntax-tokens + :semantic-tokens-client-capabilities-server-cancel-support + :semantic-tokens-client-capabilities-multiline-token-support + :semantic-tokens-client-capabilities-overlapping-token-support + :semantic-tokens-client-capabilities-formats + :semantic-tokens-client-capabilities-token-modifiers + :semantic-tokens-client-capabilities-token-types + :semantic-tokens-client-capabilities-requests + :semantic-tokens-client-capabilities-dynamic-registration + :call-hierarchy-client-capabilities + :call-hierarchy-client-capabilities-dynamic-registration + :code-description-support + :version-support + :publish-diagnostics-client-capabilities + :publish-diagnostics-client-capabilities-data-support + :publish-diagnostics-client-capabilities-code-description-support + :publish-diagnostics-client-capabilities-version-support + :publish-diagnostics-client-capabilities-tag-support + :publish-diagnostics-client-capabilities-related-information + :selection-range-client-capabilities + :selection-range-client-capabilities-dynamic-registration + :line-folding-only + :range-limit + :folding-range-client-capabilities + :folding-range-client-capabilities-folding-range + :folding-range-client-capabilities-folding-range-kind + :folding-range-client-capabilities-line-folding-only + :folding-range-client-capabilities-range-limit + :folding-range-client-capabilities-dynamic-registration + :prepare-support + :rename-client-capabilities + :rename-client-capabilities-honors-change-annotations + :rename-client-capabilities-prepare-support-default-behavior + :rename-client-capabilities-prepare-support + :rename-client-capabilities-dynamic-registration + :document-on-type-formatting-client-capabilities + :document-on-type-formatting-client-capabilities-dynamic-registration + :document-range-formatting-client-capabilities + :document-range-formatting-client-capabilities-ranges-support + :document-range-formatting-client-capabilities-dynamic-registration + :document-formatting-client-capabilities + :document-formatting-client-capabilities-dynamic-registration + :document-color-client-capabilities + :document-color-client-capabilities-dynamic-registration + :tooltip-support + :document-link-client-capabilities + :document-link-client-capabilities-tooltip-support + :document-link-client-capabilities-dynamic-registration + :code-lens-client-capabilities + :code-lens-client-capabilities-dynamic-registration + :honors-change-annotations + :data-support + :disabled-support + :is-preferred-support + :code-action-literal-support + :code-action-client-capabilities + :code-action-client-capabilities-honors-change-annotations + :code-action-client-capabilities-resolve-support + :code-action-client-capabilities-data-support + :code-action-client-capabilities-disabled-support + :code-action-client-capabilities-is-preferred-support + :code-action-client-capabilities-code-action-literal-support + :code-action-client-capabilities-dynamic-registration + :label-support + :hierarchical-document-symbol-support + :document-symbol-client-capabilities + :document-symbol-client-capabilities-label-support + :document-symbol-client-capabilities-tag-support + :document-symbol-client-capabilities-hierarchical-document-symbol-support + :document-symbol-client-capabilities-symbol-kind + :document-symbol-client-capabilities-dynamic-registration + :document-highlight-client-capabilities + :document-highlight-client-capabilities-dynamic-registration + :reference-client-capabilities + :reference-client-capabilities-dynamic-registration + :implementation-client-capabilities + :implementation-client-capabilities-link-support + :implementation-client-capabilities-dynamic-registration + :type-definition-client-capabilities + :type-definition-client-capabilities-link-support + :type-definition-client-capabilities-dynamic-registration + :definition-client-capabilities + :definition-client-capabilities-link-support + :definition-client-capabilities-dynamic-registration + :link-support + :declaration-client-capabilities + :declaration-client-capabilities-link-support + :declaration-client-capabilities-dynamic-registration + :signature-help-client-capabilities + :signature-help-client-capabilities-context-support + :signature-help-client-capabilities-signature-information + :signature-help-client-capabilities-dynamic-registration + :content-format + :hover-client-capabilities + :hover-client-capabilities-content-format + :hover-client-capabilities-dynamic-registration + :context-support + :completion-client-capabilities + :completion-client-capabilities-completion-list + :completion-client-capabilities-context-support + :completion-client-capabilities-insert-text-mode + :completion-client-capabilities-completion-item-kind + :completion-client-capabilities-completion-item + :completion-client-capabilities-dynamic-registration + :did-save + :text-document-sync-client-capabilities + :text-document-sync-client-capabilities-did-save + :text-document-sync-client-capabilities-will-save-wait-until + :text-document-sync-client-capabilities-will-save + :text-document-sync-client-capabilities-dynamic-registration + :folding-range-workspace-client-capabilities + :folding-range-workspace-client-capabilities-refresh-support + :diagnostic-workspace-client-capabilities + :diagnostic-workspace-client-capabilities-refresh-support + :inlay-hint-workspace-client-capabilities + :inlay-hint-workspace-client-capabilities-refresh-support + :inline-value-workspace-client-capabilities + :inline-value-workspace-client-capabilities-refresh-support + :file-operation-client-capabilities + :file-operation-client-capabilities-will-delete + :file-operation-client-capabilities-did-delete + :file-operation-client-capabilities-will-rename + :file-operation-client-capabilities-did-rename + :file-operation-client-capabilities-will-create + :file-operation-client-capabilities-did-create + :file-operation-client-capabilities-dynamic-registration + :code-lens-workspace-client-capabilities + :code-lens-workspace-client-capabilities-refresh-support + :refresh-support + :semantic-tokens-workspace-client-capabilities + :semantic-tokens-workspace-client-capabilities-refresh-support + :execute-command-client-capabilities + :execute-command-client-capabilities-dynamic-registration + :resolve-support + :tag-support + :workspace-symbol-client-capabilities + :workspace-symbol-client-capabilities-resolve-support + :workspace-symbol-client-capabilities-tag-support + :workspace-symbol-client-capabilities-symbol-kind + :workspace-symbol-client-capabilities-dynamic-registration + :relative-pattern-support + :did-change-watched-files-client-capabilities + :did-change-watched-files-client-capabilities-relative-pattern-support + :did-change-watched-files-client-capabilities-dynamic-registration + :dynamic-registration + :did-change-configuration-client-capabilities + :did-change-configuration-client-capabilities-dynamic-registration + :change-annotation-support + :normalizes-line-endings + :failure-handling + :resource-operations + :workspace-edit-client-capabilities + :workspace-edit-client-capabilities-change-annotation-support + :workspace-edit-client-capabilities-normalizes-line-endings + :workspace-edit-client-capabilities-failure-handling + :workspace-edit-client-capabilities-resource-operations + :workspace-edit-client-capabilities-document-changes + :base-uri + :relative-pattern + :relative-pattern-pattern + :relative-pattern-base-uri + :position-encodings + :markdown + :regular-expressions + :stale-request-support + :general-client-capabilities + :general-client-capabilities-position-encodings + :general-client-capabilities-markdown + :general-client-capabilities-regular-expressions + :general-client-capabilities-stale-request-support + :show-document + :show-message + :window-client-capabilities + :window-client-capabilities-show-document + :window-client-capabilities-show-message + :window-client-capabilities-work-done-progress + :notebook-document-client-capabilities + :notebook-document-client-capabilities-synchronization + :inline-completion + :type-hierarchy + :linked-editing-range + :call-hierarchy + :publish-diagnostics + :rename + :on-type-formatting + :range-formatting + :formatting + :references + :implementation + :type-definition + :definition + :declaration + :completion + :synchronization + :text-document-client-capabilities + :text-document-client-capabilities-inline-completion + :text-document-client-capabilities-diagnostic + :text-document-client-capabilities-inlay-hint + :text-document-client-capabilities-inline-value + :text-document-client-capabilities-type-hierarchy + :text-document-client-capabilities-moniker + :text-document-client-capabilities-linked-editing-range + :text-document-client-capabilities-semantic-tokens + :text-document-client-capabilities-call-hierarchy + :text-document-client-capabilities-publish-diagnostics + :text-document-client-capabilities-selection-range + :text-document-client-capabilities-folding-range + :text-document-client-capabilities-rename + :text-document-client-capabilities-on-type-formatting + :text-document-client-capabilities-range-formatting + :text-document-client-capabilities-formatting + :text-document-client-capabilities-color-provider + :text-document-client-capabilities-document-link + :text-document-client-capabilities-code-lens + :text-document-client-capabilities-code-action + :text-document-client-capabilities-document-symbol + :text-document-client-capabilities-document-highlight + :text-document-client-capabilities-references + :text-document-client-capabilities-implementation + :text-document-client-capabilities-type-definition + :text-document-client-capabilities-definition + :text-document-client-capabilities-declaration + :text-document-client-capabilities-signature-help + :text-document-client-capabilities-hover + :text-document-client-capabilities-completion + :text-document-client-capabilities-synchronization + :inline-value + :file-operations + :configuration + :execute-command + :symbol + :did-change-watched-files + :did-change-configuration + :apply-edit + :workspace-client-capabilities + :workspace-client-capabilities-folding-range + :workspace-client-capabilities-diagnostics + :workspace-client-capabilities-inlay-hint + :workspace-client-capabilities-inline-value + :workspace-client-capabilities-file-operations + :workspace-client-capabilities-code-lens + :workspace-client-capabilities-semantic-tokens + :workspace-client-capabilities-configuration + :workspace-client-capabilities-workspace-folders + :workspace-client-capabilities-execute-command + :workspace-client-capabilities-symbol + :workspace-client-capabilities-did-change-watched-files + :workspace-client-capabilities-did-change-configuration + :workspace-client-capabilities-workspace-edit + :workspace-client-capabilities-apply-edit + :execution-order + :execution-summary-success + :execution-summary-execution-order + :ignore-case + :file-operation-pattern-options-ignore-case + :language + :notebook + :notebook-cell-text-document-filter + :notebook-cell-text-document-filter-language + :notebook-cell-text-document-filter-notebook + :parameter-information + :parameter-information-documentation + :parameter-information-label + :diagnostic-related-information-message + :diagnostic-related-information-location + :href + :code-description-href + :will-delete + :did-delete + :will-rename + :did-rename + :will-create + :did-create + :file-operation-options + :file-operation-options-will-delete + :file-operation-options-did-delete + :file-operation-options-will-rename + :file-operation-options-did-rename + :file-operation-options-will-create + :file-operation-options-did-create + :change-notifications + :supported + :workspace-folders-server-capabilities + :workspace-folders-server-capabilities-change-notifications + :workspace-folders-server-capabilities-supported + :notebook-document-sync-registration-options + :notebook-selector + :notebook-document-sync-options + :notebook-document-sync-options-save + :notebook-document-sync-options-notebook-selector + :save + :will-save-wait-until + :will-save + :open-close + :text-document-sync-options + :text-document-sync-options-save + :text-document-sync-options-will-save-wait-until + :text-document-sync-options-will-save + :text-document-sync-options-change + :text-document-sync-options-open-close + :general + :window + :client-capabilities + :client-capabilities-experimental + :client-capabilities-general + :client-capabilities-window + :client-capabilities-notebook-document + :client-capabilities-text-document + :client-capabilities-workspace + :selected-completion-info-text + :selected-completion-info-range + :notebook-cell-array-change + :notebook-cell-array-change-cells + :notebook-cell-array-change-delete-count + :notebook-cell-array-change-start + :execution-summary + :document + :notebook-cell + :notebook-cell-execution-summary + :notebook-cell-metadata + :notebook-cell-document + :workspace-unchanged-document-diagnostic-report + :workspace-unchanged-document-diagnostic-report-version + :workspace-unchanged-document-diagnostic-report-uri + :workspace-full-document-diagnostic-report + :workspace-full-document-diagnostic-report-version + :workspace-full-document-diagnostic-report-uri + :matches + :glob + :file-operation-pattern + :file-operation-pattern-options + :file-operation-pattern-matches + :file-operation-pattern-glob + :ignore-if-not-exists + :recursive + :delete-file-options-ignore-if-not-exists + :delete-file-options-recursive + :rename-file-options-ignore-if-exists + :rename-file-options-overwrite + :ignore-if-exists + :overwrite + :create-file-options-ignore-if-exists + :create-file-options-overwrite + :resource-operation + :resource-operation-annotation-id + :annotation-id + :annotated-text-edit + :annotated-text-edit-annotation-id + :optional-versioned-text-document-identifier + :optional-versioned-text-document-identifier-version + :token-modifiers + :token-types + :semantic-tokens-legend + :semantic-tokens-legend-token-modifiers + :semantic-tokens-legend-token-types + :commands + :execute-command-options + :execute-command-options-commands + :prepare-provider + :rename-options + :rename-options-prepare-provider + :more-trigger-character + :first-trigger-character + :document-on-type-formatting-options + :document-on-type-formatting-options-more-trigger-character + :document-on-type-formatting-options-first-trigger-character + :ranges-support + :document-range-formatting-options + :document-range-formatting-options-ranges-support + :document-formatting-options + :trim-final-newlines + :insert-final-newline + :trim-trailing-whitespace + :insert-spaces + :tab-size + :formatting-options + :formatting-options-trim-final-newlines + :formatting-options-insert-final-newline + :formatting-options-trim-trailing-whitespace + :formatting-options-insert-spaces + :formatting-options-tab-size + :document-link-options + :document-link-options-resolve-provider + :code-lens-options + :code-lens-options-resolve-provider + :workspace-symbol-options + :workspace-symbol-options-resolve-provider + :code-action-kinds + :code-action-options + :code-action-options-resolve-provider + :code-action-options-code-action-kinds + :only + :code-action-context + :code-action-context-trigger-kind + :code-action-context-only + :code-action-context-diagnostics + :document-symbol-options + :document-symbol-options-label + :container-name + :base-symbol-information + :base-symbol-information-container-name + :base-symbol-information-tags + :base-symbol-information-kind + :base-symbol-information-name + :document-highlight-options + :reference-options + :include-declaration + :reference-context + :reference-context-include-declaration + :definition-options + :retrigger-characters + :signature-help-options + :signature-help-options-retrigger-characters + :signature-help-options-trigger-characters + :parameters + :signature-information + :signature-information-active-parameter + :signature-information-parameters + :signature-information-documentation + :signature-information-label + :active-signature-help + :is-retrigger + :signature-help-context + :signature-help-context-active-signature-help + :signature-help-context-is-retrigger + :signature-help-context-trigger-character + :signature-help-context-trigger-kind + :hover-options + :all-commit-characters + :trigger-characters + :completion-options + :completion-options-completion-item + :completion-options-resolve-provider + :completion-options-all-commit-characters + :completion-options-trigger-characters + :replace + :insert + :insert-replace-edit + :insert-replace-edit-replace + :insert-replace-edit-insert + :insert-replace-edit-new-text + :completion-item-label-details-description + :completion-item-label-details-detail + :trigger-character + :completion-context + :completion-context-trigger-character + :completion-context-trigger-kind + :related-information + :source + :code-description + :code + :severity + :diagnostic + :diagnostic-data + :diagnostic-related-information + :diagnostic-tags + :diagnostic-message + :diagnostic-source + :diagnostic-code-description + :diagnostic-code + :diagnostic-range + :glob-pattern + :file-system-watcher + :file-system-watcher-kind + :file-system-watcher-glob-pattern + :file-event + :file-event-type + :file-event-uri + :include-text + :save-options + :save-options-include-text + :versioned-text-document-identifier + :versioned-text-document-identifier-version + :experimental + :workspace + :inline-completion-provider + :diagnostic-provider + :inlay-hint-provider + :inline-value-provider + :type-hierarchy-provider + :moniker-provider + :semantic-tokens-provider + :linked-editing-range-provider + :call-hierarchy-provider + :execute-command-provider + :selection-range-provider + :folding-range-provider + :rename-provider + :document-on-type-formatting-provider + :document-range-formatting-provider + :document-formatting-provider + :workspace-symbol-provider + :color-provider + :document-link-provider + :code-lens-provider + :code-action-provider + :document-symbol-provider + :document-highlight-provider + :references-provider + :implementation-provider + :type-definition-provider + :definition-provider + :declaration-provider + :signature-help-provider + :hover-provider + :completion-provider + :notebook-document-sync + :text-document-sync + :position-encoding + :server-capabilities + :server-capabilities-experimental + :server-capabilities-workspace + :server-capabilities-inline-completion-provider + :server-capabilities-diagnostic-provider + :server-capabilities-inlay-hint-provider + :server-capabilities-inline-value-provider + :server-capabilities-type-hierarchy-provider + :server-capabilities-moniker-provider + :server-capabilities-semantic-tokens-provider + :server-capabilities-linked-editing-range-provider + :server-capabilities-call-hierarchy-provider + :server-capabilities-execute-command-provider + :server-capabilities-selection-range-provider + :server-capabilities-folding-range-provider + :server-capabilities-rename-provider + :server-capabilities-document-on-type-formatting-provider + :server-capabilities-document-range-formatting-provider + :server-capabilities-document-formatting-provider + :server-capabilities-workspace-symbol-provider + :server-capabilities-color-provider + :server-capabilities-document-link-provider + :server-capabilities-code-lens-provider + :server-capabilities-code-action-provider + :server-capabilities-document-symbol-provider + :server-capabilities-document-highlight-provider + :server-capabilities-references-provider + :server-capabilities-implementation-provider + :server-capabilities-type-definition-provider + :server-capabilities-definition-provider + :server-capabilities-declaration-provider + :server-capabilities-signature-help-provider + :server-capabilities-hover-provider + :server-capabilities-completion-provider + :server-capabilities-notebook-document-sync + :server-capabilities-text-document-sync + :server-capabilities-position-encoding + :workspace-folders + :workspace-folders-initialize-params + :workspace-folders-initialize-params-workspace-folders + :trace + :initialization-options + :root-uri + :root-path + :locale + :client-info + :process-id + :_initialize-params + :_initialize-params-trace + :_initialize-params-initialization-options + :_initialize-params-capabilities + :_initialize-params-root-uri + :_initialize-params-root-path + :_initialize-params-locale + :_initialize-params-client-info + :_initialize-params-process-id + :unregistration + :unregistration-method + :unregistration-id + :register-options + :method + :registration + :registration-register-options + :registration-method + :registration-id + :inline-completion-options + :string-value + :string-value-value + :string-value-kind + :selected-completion-info + :trigger-kind + :inline-completion-context + :inline-completion-context-selected-completion-info + :inline-completion-context-trigger-kind + :notebook-document-identifier + :notebook-document-identifier-uri + :notebook-document-change-event + :notebook-document-change-event-cells + :notebook-document-change-event-metadata + :versioned-notebook-document-identifier + :versioned-notebook-document-identifier-uri + :versioned-notebook-document-identifier-version + :language-id + :text-document-item + :text-document-item-text + :text-document-item-version + :text-document-item-language-id + :text-document-item-uri + :cells + :metadata + :notebook-type + :notebook-document-cells + :notebook-document-metadata + :notebook-document-version + :notebook-document-notebook-type + :notebook-document-uri + :previous-result-id-value + :previous-result-id-uri + :workspace-diagnostics + :inter-file-dependencies + :diagnostic-options + :diagnostic-options-workspace-diagnostics + :diagnostic-options-inter-file-dependencies + :diagnostic-options-identifier + :unchanged-document-diagnostic-report + :unchanged-document-diagnostic-report-result-id + :unchanged-document-diagnostic-report-kind + :full-document-diagnostic-report + :full-document-diagnostic-report-items + :full-document-diagnostic-report-result-id + :full-document-diagnostic-report-kind + :related-unchanged-document-diagnostic-report + :related-unchanged-document-diagnostic-report-related-documents + :related-full-document-diagnostic-report + :related-full-document-diagnostic-report-related-documents + :resolve-provider + :inlay-hint-options + :inlay-hint-options-resolve-provider + :markup-content + :markup-content-value + :markup-content-kind + :inlay-hint-label-part + :inlay-hint-label-part-command + :inlay-hint-label-part-location + :inlay-hint-label-part-tooltip + :inlay-hint-label-part-value + :inline-value-options + :expression + :inline-value-evaluatable-expression + :inline-value-evaluatable-expression-expression + :inline-value-evaluatable-expression-range + :case-sensitive-lookup + :variable-name + :inline-value-variable-lookup + :inline-value-variable-lookup-case-sensitive-lookup + :inline-value-variable-lookup-variable-name + :inline-value-variable-lookup-range + :inline-value-text + :inline-value-text-text + :inline-value-text-range + :stopped-location + :frame-id + :inline-value-context + :inline-value-context-stopped-location + :inline-value-context-frame-id + :type-hierarchy-options + :moniker-options + :file-delete + :file-delete-uri + :file-rename + :file-rename-new-uri + :file-rename-old-uri + :pattern + :file-operation-filter + :file-operation-filter-pattern + :file-operation-filter-scheme + :description + :needs-confirmation + :change-annotation + :change-annotation-description + :change-annotation-needs-confirmation + :change-annotation-label + :delete-file + :delete-file-options + :delete-file-uri + :delete-file-kind + :new-uri + :old-uri + :rename-file + :rename-file-options + :rename-file-new-uri + :rename-file-old-uri + :rename-file-kind + :create-file + :create-file-options + :create-file-uri + :create-file-kind + :text-document-edit + :text-document-edit-edits + :text-document-edit-text-document + :file-create + :file-create-uri + :linked-editing-range-options + :delete-count + :semantic-tokens-edit + :semantic-tokens-edit-data + :semantic-tokens-edit-delete-count + :semantic-tokens-edit-start + :full + :legend + :semantic-tokens-options + :semantic-tokens-options-full + :semantic-tokens-options-range + :semantic-tokens-options-legend + :call-hierarchy-options + :selection-range-options + :character + :line + :position-character + :position-line + :declaration-options + :folding-range-options + :document-color-options + :alpha + :blue + :green + :red + :color-alpha + :color-blue + :color-green + :color-red + :text-document-identifier + :text-document-identifier-uri + :scope-uri + :configuration-item + :configuration-item-section + :configuration-item-scope-uri + :removed + :added + :workspace-folders-change-event + :workspace-folders-change-event-removed + :workspace-folders-change-event-added + :type-definition-options + :static-registration-options + :static-registration-options-id + :implementation-options + :end + :start + :range-end + :range-start + :target-selection-range + :target-range + :target-uri + :origin-selection-range + :location-link + :location-link-target-selection-range + :location-link-target-range + :location-link-target-uri + :location-link-origin-selection-range + :partial-result-token + :partial-result-params + :partial-result-params-partial-result-token + :work-done-token + :work-done-progress-params + :work-done-progress-params-work-done-token + :text-document-position-params + :text-document-position-params-position + :text-document-position-params-text-document + :progress-params + :progress-params-value + :progress-params-token + :id + :cancel-params + :cancel-params-id + :verbose + :log-trace-params + :log-trace-params-verbose + :log-trace-params-message + :value + :set-trace-params + :set-trace-params-value + :work-done-progress-end + :work-done-progress-end-message + :work-done-progress-end-kind + :work-done-progress-report + :work-done-progress-report-percentage + :work-done-progress-report-message + :work-done-progress-report-cancellable + :work-done-progress-report-kind + :percentage + :cancellable + :work-done-progress-begin + :work-done-progress-begin-percentage + :work-done-progress-begin-message + :work-done-progress-begin-cancellable + :work-done-progress-begin-title + :work-done-progress-begin-kind + :failed-change + :failure-reason + :applied + :apply-workspace-edit-result + :apply-workspace-edit-result-failed-change + :apply-workspace-edit-result-failure-reason + :apply-workspace-edit-result-applied + :apply-workspace-edit-params + :apply-workspace-edit-params-edit + :apply-workspace-edit-params-label + :execute-command-registration-options + :execute-command-params + :execute-command-params-arguments + :execute-command-params-command + :prepare-rename-params + :rename-registration-options + :new-name + :rename-params + :rename-params-new-name + :rename-params-position + :rename-params-text-document + :document-on-type-formatting-registration-options + :ch + :document-on-type-formatting-params + :document-on-type-formatting-params-options + :document-on-type-formatting-params-ch + :document-on-type-formatting-params-position + :document-on-type-formatting-params-text-document + :document-ranges-formatting-params + :document-ranges-formatting-params-options + :document-ranges-formatting-params-ranges + :document-ranges-formatting-params-text-document + :document-range-formatting-registration-options + :document-range-formatting-params + :document-range-formatting-params-options + :document-range-formatting-params-range + :document-range-formatting-params-text-document + :document-formatting-registration-options + :options + :document-formatting-params + :document-formatting-params-options + :document-formatting-params-text-document + :document-link-registration-options + :target + :document-link + :document-link-data + :document-link-tooltip + :document-link-target + :document-link-range + :document-link-params + :document-link-params-text-document + :code-lens-registration-options + :code-lens + :code-lens-data + :code-lens-command + :code-lens-range + :code-lens-params + :code-lens-params-text-document + :workspace-symbol-registration-options + :workspace-symbol + :workspace-symbol-data + :workspace-symbol-location + :query + :workspace-symbol-params + :workspace-symbol-params-query + :code-action-registration-options + :edit + :disabled + :is-preferred + :code-action + :code-action-data + :code-action-command + :code-action-edit + :code-action-disabled + :code-action-is-preferred + :code-action-diagnostics + :code-action-title + :arguments + :command-arguments + :command-command + :command-title + :code-action-params + :code-action-params-context + :code-action-params-range + :code-action-params-text-document + :document-symbol-registration-options + :children + :document-symbol + :document-symbol-children + :document-symbol-selection-range + :document-symbol-range + :document-symbol-deprecated + :document-symbol-tags + :document-symbol-kind + :document-symbol-detail + :document-symbol-name + :symbol-information + :symbol-information-location + :symbol-information-deprecated + :document-symbol-params + :document-symbol-params-text-document + :document-highlight-registration-options + :document-highlight + :document-highlight-range + :document-highlight-params + :reference-registration-options + :reference-params + :reference-params-context + :definition-registration-options + :definition-params + :signature-help-registration-options + :active-parameter + :active-signature + :signatures + :signature-help + :signature-help-active-parameter + :signature-help-active-signature + :signature-help-signatures + :signature-help-params + :signature-help-params-context + :hover-registration-options + :contents + :hover + :hover-range + :hover-contents + :hover-params + :completion-registration-options + :item-defaults + :is-incomplete + :completion-list + :completion-list-items + :completion-list-item-defaults + :completion-list-is-incomplete + :commit-characters + :text-edit-text + :sort-text + :preselect + :deprecated + :documentation + :label-details + :completion-item + :completion-item-data + :completion-item-command + :completion-item-commit-characters + :completion-item-additional-text-edits + :completion-item-text-edit-text + :completion-item-text-edit + :completion-item-insert-text-mode + :completion-item-insert-text-format + :completion-item-insert-text + :completion-item-filter-text + :completion-item-sort-text + :completion-item-preselect + :completion-item-deprecated + :completion-item-documentation + :completion-item-detail + :completion-item-tags + :completion-item-label-details + :completion-item-label + :completion-params + :completion-params-context + :diagnostics + :version + :publish-diagnostics-params + :publish-diagnostics-params-diagnostics + :publish-diagnostics-params-version + :publish-diagnostics-params-uri + :watchers + :did-change-watched-files-registration-options + :did-change-watched-files-registration-options-watchers + :did-change-watched-files-params + :did-change-watched-files-params-changes + :new-text + :text-edit-new-text + :text-edit-range + :reason + :will-save-text-document-params + :will-save-text-document-params-reason + :will-save-text-document-params-text-document + :text-document-save-registration-options + :text + :did-save-text-document-params + :did-save-text-document-params-text + :did-save-text-document-params-text-document + :did-close-text-document-params + :did-close-text-document-params-text-document + :sync-kind + :text-document-change-registration-options + :text-document-change-registration-options-sync-kind + :content-changes + :did-change-text-document-params + :did-change-text-document-params-content-changes + :did-change-text-document-params-text-document + :did-open-text-document-params + :did-open-text-document-params-text-document + :log-message-params + :log-message-params-message + :log-message-params-type + :title + :message-action-item + :message-action-item-title + :actions + :show-message-request-params + :show-message-request-params-actions + :show-message-request-params-message + :show-message-request-params-type + :message + :type + :show-message-params + :show-message-params-message + :show-message-params-type + :section + :did-change-configuration-registration-options + :did-change-configuration-registration-options-section + :settings + :did-change-configuration-params + :did-change-configuration-params-settings + :initialized-params + :retry + :initialize-error + :initialize-error-retry + :server-info + :capabilities + :initialize-result + :initialize-result-server-info + :initialize-result-capabilities + :initialize-params + :unregisterations + :unregistration-params + :unregistration-params-unregisterations + :registrations + :registration-params + :registration-params-registrations + :inline-completion-registration-options + :command + :filter-text + :insert-text + :inline-completion-item + :inline-completion-item-command + :inline-completion-item-range + :inline-completion-item-filter-text + :inline-completion-item-insert-text + :inline-completion-list + :inline-completion-list-items + :inline-completion-params + :inline-completion-params-context + :did-close-notebook-document-params + :did-close-notebook-document-params-cell-text-documents + :did-close-notebook-document-params-notebook-document + :did-save-notebook-document-params + :did-save-notebook-document-params-notebook-document + :change + :did-change-notebook-document-params + :did-change-notebook-document-params-change + :did-change-notebook-document-params-notebook-document + :cell-text-documents + :notebook-document + :did-open-notebook-document-params + :did-open-notebook-document-params-cell-text-documents + :did-open-notebook-document-params-notebook-document + :workspace-diagnostic-report-partial-result + :workspace-diagnostic-report-partial-result-items + :workspace-diagnostic-report + :workspace-diagnostic-report-items + :previous-result-ids + :workspace-diagnostic-params + :workspace-diagnostic-params-previous-result-ids + :workspace-diagnostic-params-identifier + :diagnostic-registration-options + :retrigger-request + :diagnostic-server-cancellation-data + :diagnostic-server-cancellation-data-retrigger-request + :related-documents + :document-diagnostic-report-partial-result + :document-diagnostic-report-partial-result-related-documents + :document-diagnostic-params + :document-diagnostic-params-previous-result-id + :document-diagnostic-params-identifier + :document-diagnostic-params-text-document + :inlay-hint-registration-options + :padding-right + :padding-left + :tooltip + :text-edits + :position + :inlay-hint + :inlay-hint-data + :inlay-hint-padding-right + :inlay-hint-padding-left + :inlay-hint-tooltip + :inlay-hint-text-edits + :inlay-hint-label + :inlay-hint-position + :inlay-hint-params + :inlay-hint-params-range + :inlay-hint-params-text-document + :inline-value-registration-options + :context + :inline-value-params + :inline-value-params-context + :inline-value-params-range + :inline-value-params-text-document + :type-hierarchy-subtypes-params + :type-hierarchy-subtypes-params-item + :type-hierarchy-supertypes-params + :type-hierarchy-supertypes-params-item + :type-hierarchy-registration-options + :type-hierarchy-item + :type-hierarchy-item-data + :type-hierarchy-item-selection-range + :type-hierarchy-item-range + :type-hierarchy-item-uri + :type-hierarchy-item-detail + :type-hierarchy-item-tags + :type-hierarchy-item-kind + :type-hierarchy-item-name + :type-hierarchy-prepare-params + :moniker-registration-options + :unique + :identifier + :scheme + :moniker + :moniker-unique + :moniker-identifier + :moniker-scheme + :moniker-params + :delete-files-params + :delete-files-params-files + :rename-files-params + :rename-files-params-files + :filters + :file-operation-registration-options + :file-operation-registration-options-filters + :change-annotations + :document-changes + :changes + :workspace-edit + :workspace-edit-change-annotations + :workspace-edit-document-changes + :workspace-edit-changes + :files + :create-files-params + :create-files-params-files + :linked-editing-range-registration-options + :word-pattern + :ranges + :linked-editing-ranges + :linked-editing-ranges-word-pattern + :linked-editing-ranges-ranges + :linked-editing-range-params + :success + :show-document-result + :show-document-result-success + :selection + :take-focus + :external + :show-document-params + :show-document-params-selection + :show-document-params-take-focus + :show-document-params-external + :show-document-params-uri + :semantic-tokens-range-params + :semantic-tokens-range-params-range + :semantic-tokens-range-params-text-document + :semantic-tokens-delta-partial-result + :semantic-tokens-delta-partial-result-edits + :edits + :semantic-tokens-delta + :semantic-tokens-delta-edits + :semantic-tokens-delta-result-id + :previous-result-id + :semantic-tokens-delta-params + :semantic-tokens-delta-params-previous-result-id + :semantic-tokens-delta-params-text-document + :semantic-tokens-registration-options + :semantic-tokens-partial-result + :semantic-tokens-partial-result-data + :result-id + :semantic-tokens + :semantic-tokens-data + :semantic-tokens-result-id + :semantic-tokens-params + :semantic-tokens-params-text-document + :to + :call-hierarchy-outgoing-call + :call-hierarchy-outgoing-call-from-ranges + :call-hierarchy-outgoing-call-to + :call-hierarchy-outgoing-calls-params + :call-hierarchy-outgoing-calls-params-item + :from-ranges + :from + :call-hierarchy-incoming-call + :call-hierarchy-incoming-call-from-ranges + :call-hierarchy-incoming-call-from + :item + :call-hierarchy-incoming-calls-params + :call-hierarchy-incoming-calls-params-item + :call-hierarchy-registration-options + :data + :detail + :tags + :call-hierarchy-item + :call-hierarchy-item-data + :call-hierarchy-item-selection-range + :call-hierarchy-item-range + :call-hierarchy-item-uri + :call-hierarchy-item-detail + :call-hierarchy-item-tags + :call-hierarchy-item-kind + :call-hierarchy-item-name + :call-hierarchy-prepare-params + :work-done-progress-cancel-params + :work-done-progress-cancel-params-token + :token + :work-done-progress-create-params + :work-done-progress-create-params-token + :selection-range-registration-options + :parent + :selection-range + :selection-range-parent + :selection-range-range + :positions + :selection-range-params + :selection-range-params-positions + :selection-range-params-text-document + :declaration-registration-options + :declaration-params + :folding-range-registration-options + :collapsed-text + :kind + :end-character + :end-line + :start-character + :start-line + :folding-range + :folding-range-collapsed-text + :folding-range-end-character + :folding-range-end-line + :folding-range-start-character + :folding-range-start-line + :folding-range-params + :folding-range-params-text-document + :document-selector + :text-document-registration-options + :text-document-registration-options-document-selector + :work-done-progress + :work-done-progress-options + :work-done-progress-options-work-done-progress + :additional-text-edits + :text-edit + :label + :color-presentation + :color-presentation-additional-text-edits + :color-presentation-text-edit + :color-presentation-label + :color-presentation-params + :color-presentation-params-range + :color-presentation-params-color + :color-presentation-params-text-document + :document-color-registration-options + :color + :color-information + :color-information-color + :color-information-range + :text-document + :document-color-params + :document-color-params-text-document + :items + :configuration-params + :configuration-params-items + :event + :did-change-workspace-folders-params + :did-change-workspace-folders-params-event + :name + :workspace-folder + :workspace-folder-name + :workspace-folder-uri + :type-definition-registration-options + :type-definition-params + :implementation-registration-options + :range + :uri + :location + :location-range + :location-uri + :implementation-params + :token-format-relative + :token-format + :prepare-support-default-behavior-identifier + :prepare-support-default-behavior + :failure-handling-kind-undo + :failure-handling-kind-text-only-transactional + :failure-handling-kind-transactional + :failure-handling-kind-abort + :failure-handling-kind + :resource-operation-kind-delete + :resource-operation-kind-rename + :resource-operation-kind-create + :resource-operation-kind + :notebook-cell-kind-code + :notebook-cell-kind-markup + :notebook-cell-kind + :file-operation-pattern-kind-folder + :file-operation-pattern-kind-file + :file-operation-pattern-kind + :code-action-trigger-kind-automatic + :code-action-trigger-kind-invoked + :code-action-trigger-kind + :signature-help-trigger-kind-content-change + :signature-help-trigger-kind-trigger-character + :signature-help-trigger-kind-invoked + :signature-help-trigger-kind + :completion-trigger-kind-trigger-for-incomplete-completions + :completion-trigger-kind-trigger-character + :completion-trigger-kind-invoked + :completion-trigger-kind + :diagnostic-tag-deprecated + :diagnostic-tag-unnecessary + :diagnostic-tag + :diagnostic-severity-hint + :diagnostic-severity-information + :diagnostic-severity-warning + :diagnostic-severity-error + :diagnostic-severity + :watch-kind-delete + :watch-kind-change + :watch-kind-create + :watch-kind + :file-change-type-deleted + :file-change-type-changed + :file-change-type-created + :file-change-type + :position-encoding-kind-utf32 + :position-encoding-kind-utf16 + :position-encoding-kind-utf8 + :position-encoding-kind + :inline-completion-trigger-kind-automatic + :inline-completion-trigger-kind-invoked + :inline-completion-trigger-kind + :markup-kind-markdown + :markup-kind-plain-text + :markup-kind + :trace-values-verbose + :trace-values-messages + :trace-values-off + :trace-values + :code-action-kind-source-fix-all + :code-action-kind-source-organize-imports + :code-action-kind-source + :code-action-kind-refactor-rewrite + :code-action-kind-refactor-inline + :code-action-kind-refactor-extract + :code-action-kind-refactor + :code-action-kind-quick-fix + :code-action-kind-empty + :code-action-kind + :document-highlight-kind-write + :document-highlight-kind-read + :document-highlight-kind-text + :document-highlight-kind + :insert-text-mode-adjust-indentation + :insert-text-mode-as-is + :insert-text-mode + :insert-text-format-snippet + :insert-text-format-plain-text + :insert-text-format + :completion-item-tag-deprecated + :completion-item-tag + :completion-item-kind-type-parameter + :completion-item-kind-operator + :completion-item-kind-event + :completion-item-kind-struct + :completion-item-kind-constant + :completion-item-kind-enum-member + :completion-item-kind-folder + :completion-item-kind-reference + :completion-item-kind-file + :completion-item-kind-color + :completion-item-kind-snippet + :completion-item-kind-keyword + :completion-item-kind-enum + :completion-item-kind-value + :completion-item-kind-unit + :completion-item-kind-property + :completion-item-kind-module + :completion-item-kind-interface + :completion-item-kind-class + :completion-item-kind-variable + :completion-item-kind-field + :completion-item-kind-constructor + :completion-item-kind-function + :completion-item-kind-method + :completion-item-kind-text + :completion-item-kind + :text-document-save-reason-focus-out + :text-document-save-reason-after-delay + :text-document-save-reason-manual + :text-document-save-reason + :text-document-sync-kind-incremental + :text-document-sync-kind-full + :text-document-sync-kind-none + :text-document-sync-kind + :message-type-debug + :message-type-log + :message-type-info + :message-type-warning + :message-type-error + :message-type + :inlay-hint-kind-parameter + :inlay-hint-kind-type + :inlay-hint-kind + :moniker-kind-local + :moniker-kind-export + :moniker-kind-import + :moniker-kind + :uniqueness-level-global + :uniqueness-level-scheme + :uniqueness-level-group + :uniqueness-level-project + :uniqueness-level-document + :uniqueness-level + :symbol-tag-deprecated + :symbol-tag + :symbol-kind-type-parameter + :symbol-kind-operator + :symbol-kind-event + :symbol-kind-struct + :symbol-kind-enum-member + :symbol-kind-null + :symbol-kind-key + :symbol-kind-object + :symbol-kind-array + :symbol-kind-boolean + :symbol-kind-number + :symbol-kind-string + :symbol-kind-constant + :symbol-kind-variable + :symbol-kind-function + :symbol-kind-interface + :symbol-kind-enum + :symbol-kind-constructor + :symbol-kind-field + :symbol-kind-property + :symbol-kind-method + :symbol-kind-class + :symbol-kind-package + :symbol-kind-namespace + :symbol-kind-module + :symbol-kind-file + :symbol-kind + :folding-range-kind-region + :folding-range-kind-imports + :folding-range-kind-comment + :folding-range-kind + :lsp-error-codes-request-cancelled + :lsp-error-codes-content-modified + :lsp-error-codes-server-cancelled + :lsp-error-codes-request-failed + :lsp-error-codes + :error-codes-unknown-error-code + :error-codes-server-not-initialized + :error-codes-internal-error + :error-codes-invalid-params + :error-codes-method-not-found + :error-codes-invalid-request + :error-codes-parse-error + :error-codes + :document-diagnostic-report-kind-unchanged + :document-diagnostic-report-kind-full + :document-diagnostic-report-kind + :semantic-token-modifiers-default-library + :semantic-token-modifiers-documentation + :semantic-token-modifiers-modification + :semantic-token-modifiers-async + :semantic-token-modifiers-abstract + :semantic-token-modifiers-deprecated + :semantic-token-modifiers-static + :semantic-token-modifiers-readonly + :semantic-token-modifiers-definition + :semantic-token-modifiers-declaration + :semantic-token-modifiers + :semantic-token-types-decorator + :semantic-token-types-operator + :semantic-token-types-regexp + :semantic-token-types-number + :semantic-token-types-string + :semantic-token-types-comment + :semantic-token-types-modifier + :semantic-token-types-keyword + :semantic-token-types-macro + :semantic-token-types-method + :semantic-token-types-function + :semantic-token-types-event + :semantic-token-types-enum-member + :semantic-token-types-property + :semantic-token-types-variable + :semantic-token-types-parameter + :semantic-token-types-type-parameter + :semantic-token-types-struct + :semantic-token-types-interface + :semantic-token-types-enum + :semantic-token-types-class + :semantic-token-types-type + :semantic-token-types-namespace + :semantic-token-types + :*version*)) (common-lisp:in-package :lem-lsp-base/protocol-3-17) (lem-lsp-base/type:define-enum semantic-token-types @@ -1689,7 +1732,7 @@ on an older state might still be useful for the client. If a client decides that a result is not of any use anymore the client should cancel the request.") (request-cancelled -32800 :documentation - "The client has canceled a request and a server as detected + "The client has canceled a request and a server has detected the cancel.")) (:type lem-lsp-base/type:lsp-integer)) @@ -1765,7 +1808,11 @@ variable of a function, a class not visible outside the project, ...)")) (lem-lsp-base/type:define-enum message-type ((error 1 :documentation "An error message.") (warning 2 :documentation "A warning message.") - (info 3 :documentation "An information message.") (log 4 :documentation "A log message.")) + (info 3 :documentation "An information message.") (log 4 :documentation "A log message.") + (debug 5 :documentation "A debug message. + +@since 3.18.0" + :since "3.18.0")) (:type lem-lsp-base/type:lsp-uinteger) :since "The message type") @@ -1926,15 +1973,29 @@ result literals like `Hover`, `ParameterInfo` or `CompletionItem`. Please note that `MarkupKinds` must not start with a `$`. This kinds are reserved for internal usage.") +(lem-lsp-base/type:define-enum inline-completion-trigger-kind + ((invoked 0 :documentation "Completion was triggered explicitly by a user gesture.") + (automatic 1 :documentation "Completion was triggered automatically while editing.")) + (:type lem-lsp-base/type:lsp-uinteger) + :since + "Describes how an {@link InlineCompletionItemProvider inline completion provider} was triggered. + +@since 3.18.0 +@proposed" + :since + common-lisp:t + :since + "3.18.0") + (lem-lsp-base/type:define-enum position-encoding-kind - ((utf8 "utf-8" :documentation "Character offsets count UTF-8 code units.") + ((utf8 "utf-8" :documentation "Character offsets count UTF-8 code units (e.g. bytes).") (utf16 "utf-16" :documentation "Character offsets count UTF-16 code units. This is the default and must always be supported by servers") (utf32 "utf-32" :documentation "Character offsets count UTF-32 code units. -Implementation note: these are the same as Unicode code points, +Implementation note: these are the same as Unicode codepoints, so this `PositionEncodingKind` may also be used for an encoding-agnostic representation of character offsets.")) (:type lem-lsp-base/type:lsp-string) @@ -2083,8 +2144,7 @@ according the to language's syntax rule.")) (lem-lsp-base/type:define-class location common-lisp:nil - ((uri :type lem-lsp-base/type:lsp-document-uri :initarg :uri :accessor - location-uri) + ((uri :type lem-lsp-base/type:lsp-document-uri :initarg :uri :accessor location-uri) (range :type range :initarg :range :accessor location-range)) (:documentation "Represents a location inside a resource, such as a line inside a text file.")) @@ -2103,10 +2163,10 @@ inside a text file.")) (lem-lsp-base/type:define-class workspace-folder common-lisp:nil - ((uri :type lem-lsp-base/type:lsp-uri :initarg :uri :accessor - workspace-folder-uri :documentation "The associated URI for this workspace folder.") - (name :type lem-lsp-base/type:lsp-string :initarg :name :accessor - workspace-folder-name :documentation "The name of the workspace folder. Used to refer to this + ((uri :type lem-lsp-base/type:lsp-uri :initarg :uri :accessor workspace-folder-uri :documentation + "The associated URI for this workspace folder.") + (name :type lem-lsp-base/type:lsp-string :initarg :name :accessor workspace-folder-name + :documentation "The name of the workspace folder. Used to refer to this workspace folder in the user interface.")) (:documentation "A workspace folder inside a client.")) @@ -2119,8 +2179,8 @@ workspace folder in the user interface.")) (lem-lsp-base/type:define-class configuration-params common-lisp:nil - ((items :type (lem-lsp-base/type:lsp-array configuration-item) :initarg - :items :accessor configuration-params-items)) + ((items :type (lem-lsp-base/type:lsp-array configuration-item) :initarg :items :accessor + configuration-params-items)) (:documentation "The parameters of a configuration request.")) (lem-lsp-base/type:define-class document-color-params @@ -2153,9 +2213,8 @@ workspace folder in the user interface.")) (lem-lsp-base/type:define-class color-presentation common-lisp:nil - ((label :type lem-lsp-base/type:lsp-string :initarg :label :accessor - color-presentation-label :documentation - "The label of this color presentation. It will be shown on the color + ((label :type lem-lsp-base/type:lsp-string :initarg :label :accessor color-presentation-label + :documentation "The label of this color presentation. It will be shown on the color picker header. By default this is also the text that is inserted when selecting this color presentation.") (text-edit :type text-edit :initarg :text-edit :accessor color-presentation-text-edit :optional @@ -2163,22 +2222,20 @@ this color presentation.") "An {@link TextEdit edit} which is applied to a document when selecting this presentation for the color. When `falsy` the {@link ColorPresentation.label label} is used.") - (additional-text-edits :type (lem-lsp-base/type:lsp-array text-edit) - :initarg :additional-text-edits :accessor color-presentation-additional-text-edits :optional + (additional-text-edits :type (lem-lsp-base/type:lsp-array text-edit) :initarg + :additional-text-edits :accessor color-presentation-additional-text-edits :optional common-lisp:t :documentation "An optional array of additional {@link TextEdit text edits} that are applied when selecting this color presentation. Edits must not overlap with the main {@link ColorPresentation.textEdit edit} nor with themselves."))) (lem-lsp-base/type:define-class work-done-progress-options common-lisp:nil - ((work-done-progress :type lem-lsp-base/type:lsp-boolean :initarg - :work-done-progress :accessor work-done-progress-options-work-done-progress :optional - common-lisp:t))) + ((work-done-progress :type lem-lsp-base/type:lsp-boolean :initarg :work-done-progress :accessor + work-done-progress-options-work-done-progress :optional common-lisp:t))) (lem-lsp-base/type:define-class text-document-registration-options common-lisp:nil - ((document-selector :type - (common-lisp:or document-selector lem-lsp-base/type:lsp-null) :initarg + ((document-selector :type (common-lisp:or document-selector lem-lsp-base/type:lsp-null) :initarg :document-selector :accessor text-document-registration-options-document-selector :documentation "A document selector to identify the scope of the registration. If set to null the document selector provided on the client side will be used.")) @@ -2192,27 +2249,27 @@ the document selector provided on the client side will be used.")) (lem-lsp-base/type:define-class folding-range common-lisp:nil - ((start-line :type lem-lsp-base/type:lsp-uinteger :initarg :start-line - :accessor folding-range-start-line :documentation + ((start-line :type lem-lsp-base/type:lsp-uinteger :initarg :start-line :accessor + folding-range-start-line :documentation "The zero-based start line of the range to fold. The folded area starts after the line's last character. To be valid, the end must be zero or larger and smaller than the number of lines in the document.") - (start-character :type lem-lsp-base/type:lsp-uinteger :initarg - :start-character :accessor folding-range-start-character :optional common-lisp:t :documentation + (start-character :type lem-lsp-base/type:lsp-uinteger :initarg :start-character :accessor + folding-range-start-character :optional common-lisp:t :documentation "The zero-based character offset from where the folded range starts. If not defined, defaults to the length of the start line.") (end-line :type lem-lsp-base/type:lsp-uinteger :initarg :end-line :accessor folding-range-end-line :documentation "The zero-based end line of the range to fold. The folded area ends with the line's last character. To be valid, the end must be zero or larger and smaller than the number of lines in the document.") - (end-character :type lem-lsp-base/type:lsp-uinteger :initarg :end-character - :accessor folding-range-end-character :optional common-lisp:t :documentation + (end-character :type lem-lsp-base/type:lsp-uinteger :initarg :end-character :accessor + folding-range-end-character :optional common-lisp:t :documentation "The zero-based character offset before the folded range ends. If not defined, defaults to the length of the end line.") (kind :type folding-range-kind :initarg :kind :accessor folding-range-kind :optional common-lisp:t :documentation "Describes the kind of the folding range such as `comment' or 'region'. The kind is used to categorize folding ranges and used by commands like 'Fold all comments'. See {@link FoldingRangeKind} for an enumeration of standardized kinds.") - (collapsed-text :type lem-lsp-base/type:lsp-string :initarg :collapsed-text - :accessor folding-range-collapsed-text :optional common-lisp:t :since "3.17.0" :documentation + (collapsed-text :type lem-lsp-base/type:lsp-string :initarg :collapsed-text :accessor + folding-range-collapsed-text :optional common-lisp:t :since "3.17.0" :documentation "The text that the client should show when the specified range is collapsed. If not defined or not supported by the client, a default will be chosen by the client. @@ -2238,9 +2295,8 @@ than the number of lines in the document. Clients are free to ignore invalid ran (work-done-progress-params partial-result-params) ((text-document :type text-document-identifier :initarg :text-document :accessor selection-range-params-text-document :documentation "The text document.") - (positions :type (lem-lsp-base/type:lsp-array position) :initarg :positions - :accessor selection-range-params-positions :documentation - "The positions inside the text document.")) + (positions :type (lem-lsp-base/type:lsp-array position) :initarg :positions :accessor + selection-range-params-positions :documentation "The positions inside the text document.")) (:documentation "A parameter literal used in selection range requests.")) (lem-lsp-base/type:define-class selection-range @@ -2277,18 +2333,17 @@ may have a parent selection range that contains it.")) (lem-lsp-base/type:define-class call-hierarchy-item common-lisp:nil - ((name :type lem-lsp-base/type:lsp-string :initarg :name :accessor - call-hierarchy-item-name :documentation "The name of this item.") + ((name :type lem-lsp-base/type:lsp-string :initarg :name :accessor call-hierarchy-item-name + :documentation "The name of this item.") (kind :type symbol-kind :initarg :kind :accessor call-hierarchy-item-kind :documentation "The kind of this item.") - (tags :type (lem-lsp-base/type:lsp-array symbol-tag) :initarg :tags - :accessor call-hierarchy-item-tags :optional common-lisp:t :documentation - "Tags for this item.") - (detail :type lem-lsp-base/type:lsp-string :initarg :detail :accessor - call-hierarchy-item-detail :optional common-lisp:t :documentation + (tags :type (lem-lsp-base/type:lsp-array symbol-tag) :initarg :tags :accessor + call-hierarchy-item-tags :optional common-lisp:t :documentation "Tags for this item.") + (detail :type lem-lsp-base/type:lsp-string :initarg :detail :accessor call-hierarchy-item-detail + :optional common-lisp:t :documentation "More detail for this item, e.g. the signature of a function.") - (uri :type lem-lsp-base/type:lsp-document-uri :initarg :uri :accessor - call-hierarchy-item-uri :documentation "The resource identifier of this item.") + (uri :type lem-lsp-base/type:lsp-document-uri :initarg :uri :accessor call-hierarchy-item-uri + :documentation "The resource identifier of this item.") (range :type range :initarg :range :accessor call-hierarchy-item-range :documentation "The range enclosing this symbol not including leading/trailing whitespace but everything else, e.g. comments and code.") (selection-range :type range :initarg :selection-range :accessor @@ -2325,8 +2380,8 @@ of call hierarchy. common-lisp:nil ((from :type call-hierarchy-item :initarg :from :accessor call-hierarchy-incoming-call-from :documentation "The item that makes the call.") - (from-ranges :type (lem-lsp-base/type:lsp-array range) :initarg :from-ranges - :accessor call-hierarchy-incoming-call-from-ranges :documentation + (from-ranges :type (lem-lsp-base/type:lsp-array range) :initarg :from-ranges :accessor + call-hierarchy-incoming-call-from-ranges :documentation "The ranges at which the calls appear. This is relative to the caller denoted by {@link CallHierarchyIncomingCall.from `this.from`}.")) (:since "3.16.0") @@ -2347,8 +2402,8 @@ denoted by {@link CallHierarchyIncomingCall.from `this.from`}.")) common-lisp:nil ((to :type call-hierarchy-item :initarg :to :accessor call-hierarchy-outgoing-call-to :documentation "The item that is called.") - (from-ranges :type (lem-lsp-base/type:lsp-array range) :initarg :from-ranges - :accessor call-hierarchy-outgoing-call-from-ranges :documentation + (from-ranges :type (lem-lsp-base/type:lsp-array range) :initarg :from-ranges :accessor + call-hierarchy-outgoing-call-from-ranges :documentation "The range at which this item is called. This is the range relative to the caller, e.g the item passed to {@link CallHierarchyItemProvider.provideCallHierarchyOutgoingCalls `provideCallHierarchyOutgoingCalls`} and not {@link CallHierarchyOutgoingCall.to `this.to`}.")) @@ -2373,19 +2428,15 @@ and not {@link CallHierarchyOutgoingCall.to `this.to`}.")) the client will include the result id in the next semantic token request. A server can then instead of computing all semantic tokens again simply send a delta.") - (data :type - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-uinteger) - :initarg :data :accessor semantic-tokens-data :documentation "The actual tokens.")) + (data :type (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-uinteger) :initarg :data + :accessor semantic-tokens-data :documentation "The actual tokens.")) (:since "3.16.0") (:documentation "@since 3.16.0")) (lem-lsp-base/type:define-class semantic-tokens-partial-result common-lisp:nil - ((data :type - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-uinteger) - :initarg :data :accessor semantic-tokens-partial-result-data)) + ((data :type (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-uinteger) :initarg :data + :accessor semantic-tokens-partial-result-data)) (:since "3.16.0") (:documentation "@since 3.16.0")) @@ -2399,8 +2450,8 @@ send a delta.") (work-done-progress-params partial-result-params) ((text-document :type text-document-identifier :initarg :text-document :accessor semantic-tokens-delta-params-text-document :documentation "The text document.") - (previous-result-id :type lem-lsp-base/type:lsp-string :initarg - :previous-result-id :accessor semantic-tokens-delta-params-previous-result-id :documentation + (previous-result-id :type lem-lsp-base/type:lsp-string :initarg :previous-result-id :accessor + semantic-tokens-delta-params-previous-result-id :documentation "The result id of a previous response. The result Id can either point to a full response or a delta response depending on what was received last.")) (:since "3.16.0") @@ -2410,16 +2461,16 @@ or a delta response depending on what was received last.")) common-lisp:nil ((result-id :type lem-lsp-base/type:lsp-string :initarg :result-id :accessor semantic-tokens-delta-result-id :optional common-lisp:t) - (edits :type (lem-lsp-base/type:lsp-array semantic-tokens-edit) :initarg - :edits :accessor semantic-tokens-delta-edits :documentation + (edits :type (lem-lsp-base/type:lsp-array semantic-tokens-edit) :initarg :edits :accessor + semantic-tokens-delta-edits :documentation "The semantic token edits to transform a previous result into a new result.")) (:since "3.16.0") (:documentation "@since 3.16.0")) (lem-lsp-base/type:define-class semantic-tokens-delta-partial-result common-lisp:nil - ((edits :type (lem-lsp-base/type:lsp-array semantic-tokens-edit) :initarg - :edits :accessor semantic-tokens-delta-partial-result-edits)) + ((edits :type (lem-lsp-base/type:lsp-array semantic-tokens-edit) :initarg :edits :accessor + semantic-tokens-delta-partial-result-edits)) (:since "3.16.0") (:documentation "@since 3.16.0")) @@ -2434,15 +2485,15 @@ or a delta response depending on what was received last.")) (lem-lsp-base/type:define-class show-document-params common-lisp:nil - ((uri :type lem-lsp-base/type:lsp-uri :initarg :uri :accessor - show-document-params-uri :documentation "The document uri to show.") + ((uri :type lem-lsp-base/type:lsp-uri :initarg :uri :accessor show-document-params-uri + :documentation "The uri to show.") (external :type lem-lsp-base/type:lsp-boolean :initarg :external :accessor show-document-params-external :optional common-lisp:t :documentation "Indicates to show the resource in an external program. -To show for example `https://code.visualstudio.com/` +To show, for example, `https://code.visualstudio.com/` in the default WEB browser set `external` to `true`.") - (take-focus :type lem-lsp-base/type:lsp-boolean :initarg :take-focus - :accessor show-document-params-take-focus :optional common-lisp:t :documentation + (take-focus :type lem-lsp-base/type:lsp-boolean :initarg :take-focus :accessor + show-document-params-take-focus :optional common-lisp:t :documentation "An optional property to indicate whether the editor showing the document should take focus or not. Clients might ignore this property if an external @@ -2453,7 +2504,7 @@ document. Clients might ignore the property if an external program is started or the file is not a text file.")) (:since "3.16.0") - (:documentation "Params to show a document. + (:documentation "Params to show a resource in the UI. @since 3.16.0")) @@ -2476,8 +2527,8 @@ file.")) linked-editing-ranges-ranges :documentation "A list of ranges that can be edited together. The ranges must have identical length and contain identical text content. The ranges cannot overlap.") - (word-pattern :type lem-lsp-base/type:lsp-string :initarg :word-pattern - :accessor linked-editing-ranges-word-pattern :optional common-lisp:t :documentation + (word-pattern :type lem-lsp-base/type:lsp-string :initarg :word-pattern :accessor + linked-editing-ranges-word-pattern :optional common-lisp:t :documentation "An optional word pattern (regular expression) that describes valid contents for the given ranges. If no pattern is provided, the client configuration's word pattern will be used.")) @@ -2492,8 +2543,8 @@ pattern will be used.")) (lem-lsp-base/type:define-class create-files-params common-lisp:nil - ((files :type (lem-lsp-base/type:lsp-array file-create) :initarg :files - :accessor create-files-params-files :documentation + ((files :type (lem-lsp-base/type:lsp-array file-create) :initarg :files :accessor + create-files-params-files :documentation "An array of all files/folders created in this operation.")) (:since "3.16.0") (:documentation "The parameters sent in notifications/requests for user-initiated creation of @@ -2504,7 +2555,7 @@ files. (lem-lsp-base/type:define-class workspace-edit common-lisp:nil ((changes :type - (lem-lsp-base/type:lsp-map document-uri + (lem-lsp-base/type:lsp-map lem-lsp-base/type:lsp-document-uri (lem-lsp-base/type:lsp-array text-edit)) :initarg :changes :accessor workspace-edit-changes :optional common-lisp:t :documentation "Holds changes to existing resources.") @@ -2524,9 +2575,9 @@ Whether a client supports versioned document edits is expressed via If a client neither supports `documentChanges` nor `workspace.workspaceEdit.resourceOperations` then only plain `TextEdit`s using the `changes` property are supported.") (change-annotations :type - (lem-lsp-base/type:lsp-map change-annotation-identifier change-annotation) - :initarg :change-annotations :accessor workspace-edit-change-annotations :optional - common-lisp:t :since "3.16.0" :documentation + (lem-lsp-base/type:lsp-map change-annotation-identifier change-annotation) :initarg + :change-annotations :accessor workspace-edit-change-annotations :optional common-lisp:t :since + "3.16.0" :documentation "A map of change annotations that can be referenced in `AnnotatedTextEdit`s or create, rename and delete file / folder operations. @@ -2549,9 +2600,8 @@ the client capability: `workspace.workspaceEdit.failureHandling`")) (lem-lsp-base/type:define-class file-operation-registration-options common-lisp:nil - ((filters :type (lem-lsp-base/type:lsp-array file-operation-filter) :initarg - :filters :accessor file-operation-registration-options-filters :documentation - "The actual filters.")) + ((filters :type (lem-lsp-base/type:lsp-array file-operation-filter) :initarg :filters :accessor + file-operation-registration-options-filters :documentation "The actual filters.")) (:since "3.16.0") (:documentation "The options to register for file operations. @@ -2559,8 +2609,8 @@ the client capability: `workspace.workspaceEdit.failureHandling`")) (lem-lsp-base/type:define-class rename-files-params common-lisp:nil - ((files :type (lem-lsp-base/type:lsp-array file-rename) :initarg :files - :accessor rename-files-params-files :documentation + ((files :type (lem-lsp-base/type:lsp-array file-rename) :initarg :files :accessor + rename-files-params-files :documentation "An array of all files/folders renamed in this operation. When a folder is renamed, only the folder will be included, and not its children.")) (:since "3.16.0") @@ -2571,8 +2621,8 @@ files. (lem-lsp-base/type:define-class delete-files-params common-lisp:nil - ((files :type (lem-lsp-base/type:lsp-array file-delete) :initarg :files - :accessor delete-files-params-files :documentation + ((files :type (lem-lsp-base/type:lsp-array file-delete) :initarg :files :accessor + delete-files-params-files :documentation "An array of all files/folders deleted in this operation.")) (:since "3.16.0") (:documentation "The parameters sent in notifications/requests for user-initiated deletes of @@ -2586,11 +2636,10 @@ files. (lem-lsp-base/type:define-class moniker common-lisp:nil - ((scheme :type lem-lsp-base/type:lsp-string :initarg :scheme :accessor - moniker-scheme :documentation "The scheme of the moniker. For example tsc or .Net") - (identifier :type lem-lsp-base/type:lsp-string :initarg :identifier - :accessor moniker-identifier :documentation - "The identifier of the moniker. The value is opaque in LSIF however + ((scheme :type lem-lsp-base/type:lsp-string :initarg :scheme :accessor moniker-scheme + :documentation "The scheme of the moniker. For example tsc or .Net") + (identifier :type lem-lsp-base/type:lsp-string :initarg :identifier :accessor moniker-identifier + :documentation "The identifier of the moniker. The value is opaque in LSIF however schema owners are allowed to define the structure if they want.") (unique :type uniqueness-level :initarg :unique :accessor moniker-unique :documentation "The scope in which the moniker is unique") @@ -2615,18 +2664,17 @@ schema owners are allowed to define the structure if they want.") (lem-lsp-base/type:define-class type-hierarchy-item common-lisp:nil - ((name :type lem-lsp-base/type:lsp-string :initarg :name :accessor - type-hierarchy-item-name :documentation "The name of this item.") + ((name :type lem-lsp-base/type:lsp-string :initarg :name :accessor type-hierarchy-item-name + :documentation "The name of this item.") (kind :type symbol-kind :initarg :kind :accessor type-hierarchy-item-kind :documentation "The kind of this item.") - (tags :type (lem-lsp-base/type:lsp-array symbol-tag) :initarg :tags - :accessor type-hierarchy-item-tags :optional common-lisp:t :documentation - "Tags for this item.") - (detail :type lem-lsp-base/type:lsp-string :initarg :detail :accessor - type-hierarchy-item-detail :optional common-lisp:t :documentation + (tags :type (lem-lsp-base/type:lsp-array symbol-tag) :initarg :tags :accessor + type-hierarchy-item-tags :optional common-lisp:t :documentation "Tags for this item.") + (detail :type lem-lsp-base/type:lsp-string :initarg :detail :accessor type-hierarchy-item-detail + :optional common-lisp:t :documentation "More detail for this item, e.g. the signature of a function.") - (uri :type lem-lsp-base/type:lsp-document-uri :initarg :uri :accessor - type-hierarchy-item-uri :documentation "The resource identifier of this item.") + (uri :type lem-lsp-base/type:lsp-document-uri :initarg :uri :accessor type-hierarchy-item-uri + :documentation "The resource identifier of this item.") (range :type range :initarg :range :accessor type-hierarchy-item-range :documentation "The range enclosing this symbol not including leading/trailing whitespace but everything else, e.g. comments and code.") @@ -2703,7 +2751,10 @@ requested.")) (lem-lsp-base/type:define-class inlay-hint common-lisp:nil ((position :type position :initarg :position :accessor inlay-hint-position :documentation - "The position of this hint.") + "The position of this hint. + +If multiple hints have the same position, they will be shown in the order +they appear in the response.") (label :type (common-lisp:or lem-lsp-base/type:lsp-string (lem-lsp-base/type:lsp-array inlay-hint-label-part)) @@ -2715,26 +2766,24 @@ InlayHintLabelPart label parts. (kind :type inlay-hint-kind :initarg :kind :accessor inlay-hint-kind :optional common-lisp:t :documentation "The kind of this hint. Can be omitted in which case the client should fall back to a reasonable default.") - (text-edits :type (lem-lsp-base/type:lsp-array text-edit) :initarg - :text-edits :accessor inlay-hint-text-edits :optional common-lisp:t :documentation + (text-edits :type (lem-lsp-base/type:lsp-array text-edit) :initarg :text-edits :accessor + inlay-hint-text-edits :optional common-lisp:t :documentation "Optional text edits that are performed when accepting this inlay hint. *Note* that edits are expected to change the document so that the inlay hint (or its nearest variant) is now part of the document and the inlay hint itself is now obsolete.") - (tooltip :type (common-lisp:or lem-lsp-base/type:lsp-string markup-content) - :initarg :tooltip :accessor inlay-hint-tooltip :optional common-lisp:t :documentation + (tooltip :type (common-lisp:or lem-lsp-base/type:lsp-string markup-content) :initarg :tooltip + :accessor inlay-hint-tooltip :optional common-lisp:t :documentation "The tooltip text when you hover over this item.") - (padding-left :type lem-lsp-base/type:lsp-boolean :initarg :padding-left - :accessor inlay-hint-padding-left :optional common-lisp:t :documentation - "Render padding before the hint. + (padding-left :type lem-lsp-base/type:lsp-boolean :initarg :padding-left :accessor + inlay-hint-padding-left :optional common-lisp:t :documentation "Render padding before the hint. Note: Padding should use the editor's background color, not the background color of the hint itself. That means padding can be used to visually align/separate an inlay hint.") - (padding-right :type lem-lsp-base/type:lsp-boolean :initarg :padding-right - :accessor inlay-hint-padding-right :optional common-lisp:t :documentation - "Render padding after the hint. + (padding-right :type lem-lsp-base/type:lsp-boolean :initarg :padding-right :accessor + inlay-hint-padding-right :optional common-lisp:t :documentation "Render padding after the hint. Note: Padding should use the editor's background color, not the background color of the hint itself. That means padding can be used @@ -2759,12 +2808,12 @@ a `textDocument/inlayHint` and a `inlayHint/resolve` request.")) (work-done-progress-params partial-result-params) ((text-document :type text-document-identifier :initarg :text-document :accessor document-diagnostic-params-text-document :documentation "The text document.") - (identifier :type lem-lsp-base/type:lsp-string :initarg :identifier - :accessor document-diagnostic-params-identifier :optional common-lisp:t :documentation + (identifier :type lem-lsp-base/type:lsp-string :initarg :identifier :accessor + document-diagnostic-params-identifier :optional common-lisp:t :documentation "The additional identifier provided during registration.") - (previous-result-id :type lem-lsp-base/type:lsp-string :initarg - :previous-result-id :accessor document-diagnostic-params-previous-result-id :optional - common-lisp:t :documentation "The result id of a previous response if provided.")) + (previous-result-id :type lem-lsp-base/type:lsp-string :initarg :previous-result-id :accessor + document-diagnostic-params-previous-result-id :optional common-lisp:t :documentation + "The result id of a previous response if provided.")) (:since "3.17.0") (:documentation "Parameters of the document diagnostic request. @@ -2773,7 +2822,7 @@ a `textDocument/inlayHint` and a `inlayHint/resolve` request.")) (lem-lsp-base/type:define-class document-diagnostic-report-partial-result common-lisp:nil ((related-documents :type - (lem-lsp-base/type:lsp-map document-uri + (lem-lsp-base/type:lsp-map lem-lsp-base/type:lsp-document-uri (common-lisp:or full-document-diagnostic-report unchanged-document-diagnostic-report)) :initarg :related-documents :accessor document-diagnostic-report-partial-result-related-documents)) @@ -2784,8 +2833,8 @@ a `textDocument/inlayHint` and a `inlayHint/resolve` request.")) (lem-lsp-base/type:define-class diagnostic-server-cancellation-data common-lisp:nil - ((retrigger-request :type lem-lsp-base/type:lsp-boolean :initarg - :retrigger-request :accessor diagnostic-server-cancellation-data-retrigger-request)) + ((retrigger-request :type lem-lsp-base/type:lsp-boolean :initarg :retrigger-request :accessor + diagnostic-server-cancellation-data-retrigger-request)) (:since "3.17.0") (:documentation "Cancellation data returned from a diagnostic request. @@ -2801,12 +2850,12 @@ a `textDocument/inlayHint` and a `inlayHint/resolve` request.")) (lem-lsp-base/type:define-class workspace-diagnostic-params (work-done-progress-params partial-result-params) - ((identifier :type lem-lsp-base/type:lsp-string :initarg :identifier - :accessor workspace-diagnostic-params-identifier :optional common-lisp:t :documentation + ((identifier :type lem-lsp-base/type:lsp-string :initarg :identifier :accessor + workspace-diagnostic-params-identifier :optional common-lisp:t :documentation "The additional identifier provided during registration.") - (previous-result-ids :type (lem-lsp-base/type:lsp-array previous-result-id) - :initarg :previous-result-ids :accessor workspace-diagnostic-params-previous-result-ids - :documentation "The currently known diagnostic reports with their + (previous-result-ids :type (lem-lsp-base/type:lsp-array previous-result-id) :initarg + :previous-result-ids :accessor workspace-diagnostic-params-previous-result-ids :documentation + "The currently known diagnostic reports with their previous result ids.")) (:since "3.17.0") (:documentation "Parameters of the workspace diagnostic request. @@ -2815,9 +2864,8 @@ previous result ids.")) (lem-lsp-base/type:define-class workspace-diagnostic-report common-lisp:nil - ((items :type - (lem-lsp-base/type:lsp-array workspace-document-diagnostic-report) :initarg - :items :accessor workspace-diagnostic-report-items)) + ((items :type (lem-lsp-base/type:lsp-array workspace-document-diagnostic-report) :initarg :items + :accessor workspace-diagnostic-report-items)) (:since "3.17.0") (:documentation "A workspace diagnostic report. @@ -2825,9 +2873,8 @@ previous result ids.")) (lem-lsp-base/type:define-class workspace-diagnostic-report-partial-result common-lisp:nil - ((items :type - (lem-lsp-base/type:lsp-array workspace-document-diagnostic-report) :initarg - :items :accessor workspace-diagnostic-report-partial-result-items)) + ((items :type (lem-lsp-base/type:lsp-array workspace-document-diagnostic-report) :initarg :items + :accessor workspace-diagnostic-report-partial-result-items)) (:since "3.17.0") (:documentation "A partial result for a workspace diagnostic report. @@ -2838,8 +2885,8 @@ previous result ids.")) ((notebook-document :type notebook-document :initarg :notebook-document :accessor did-open-notebook-document-params-notebook-document :documentation "The notebook document that got opened.") - (cell-text-documents :type (lem-lsp-base/type:lsp-array text-document-item) - :initarg :cell-text-documents :accessor did-open-notebook-document-params-cell-text-documents + (cell-text-documents :type (lem-lsp-base/type:lsp-array text-document-item) :initarg + :cell-text-documents :accessor did-open-notebook-document-params-cell-text-documents :documentation "The text documents that represent the content of a notebook cell.")) (:since "3.17.0") @@ -2890,25 +2937,80 @@ To mirror the content of a notebook using change events use the following approa ((notebook-document :type notebook-document-identifier :initarg :notebook-document :accessor did-close-notebook-document-params-notebook-document :documentation "The notebook document that got closed.") - (cell-text-documents :type - (lem-lsp-base/type:lsp-array text-document-identifier) :initarg + (cell-text-documents :type (lem-lsp-base/type:lsp-array text-document-identifier) :initarg :cell-text-documents :accessor did-close-notebook-document-params-cell-text-documents :documentation "The text documents that represent the content of a notebook cell that got closed.")) (:since "3.17.0") (:documentation "The params sent in a close notebook document notification. -@since 3.17.0")) +@since 3.17.0")) + +(lem-lsp-base/type:define-class inline-completion-params + (text-document-position-params work-done-progress-params) + ((context :type inline-completion-context :initarg :context :accessor + inline-completion-params-context :documentation + "Additional information about the context in which inline completions were +requested.")) + (:proposed common-lisp:t) + (:since "3.18.0") + (:documentation "A parameter literal used in inline completion requests. + +@since 3.18.0 +@proposed")) + +(lem-lsp-base/type:define-class inline-completion-list + common-lisp:nil + ((items :type (lem-lsp-base/type:lsp-array inline-completion-item) :initarg :items :accessor + inline-completion-list-items :documentation "The inline completion items")) + (:proposed common-lisp:t) + (:since "3.18.0") + (:documentation + "Represents a collection of {@link InlineCompletionItem inline completion items} to be presented in the editor. + +@since 3.18.0 +@proposed")) + +(lem-lsp-base/type:define-class inline-completion-item + common-lisp:nil + ((insert-text :type (common-lisp:or lem-lsp-base/type:lsp-string string-value) :initarg + :insert-text :accessor inline-completion-item-insert-text :documentation + "The text to replace the range with. Must be set.") + (filter-text :type lem-lsp-base/type:lsp-string :initarg :filter-text :accessor + inline-completion-item-filter-text :optional common-lisp:t :documentation + "A text that is used to decide if this inline completion should be shown. When `falsy` the {@link InlineCompletionItem.insertText} is used.") + (range :type range :initarg :range :accessor inline-completion-item-range :optional + common-lisp:t :documentation "The range to replace. Must begin and end on the same line.") + (command :type command :initarg :command :accessor inline-completion-item-command :optional + common-lisp:t :documentation + "An optional {@link Command} that is executed *after* inserting this completion.")) + (:proposed common-lisp:t) + (:since "3.18.0") + (:documentation + "An inline completion item represents a text snippet that is proposed inline to complete text that is being typed. + +@since 3.18.0 +@proposed")) + +(lem-lsp-base/type:define-class inline-completion-registration-options + (inline-completion-options text-document-registration-options static-registration-options) + common-lisp:nil + (:proposed common-lisp:t) + (:since "3.18.0") + (:documentation "Inline completion options used during static or dynamic registration. + +@since 3.18.0 +@proposed")) (lem-lsp-base/type:define-class registration-params common-lisp:nil - ((registrations :type (lem-lsp-base/type:lsp-array registration) :initarg - :registrations :accessor registration-params-registrations))) + ((registrations :type (lem-lsp-base/type:lsp-array registration) :initarg :registrations + :accessor registration-params-registrations))) (lem-lsp-base/type:define-class unregistration-params common-lisp:nil - ((unregisterations :type (lem-lsp-base/type:lsp-array unregistration) - :initarg :unregisterations :accessor unregistration-params-unregisterations))) + ((unregisterations :type (lem-lsp-base/type:lsp-array unregistration) :initarg :unregisterations + :accessor unregistration-params-unregisterations))) (lem-lsp-base/type:define-class initialize-params (_initialize-params workspace-folders-initialize-params) @@ -2922,8 +3024,8 @@ of a notebook cell that got closed.")) (lem-lsp-base/type:lsp-interface ((name :type lem-lsp-base/type:lsp-string :documentation "The name of the server as defined by the server.") - (version :type lem-lsp-base/type:lsp-string :optional common-lisp:t - :documentation "The server's version as defined by the server."))) + (version :type lem-lsp-base/type:lsp-string :optional common-lisp:t :documentation + "The server's version as defined by the server."))) :initarg :server-info :accessor initialize-result-server-info :optional common-lisp:t :since "3.15.0" :documentation "Information about the server. @@ -2932,9 +3034,8 @@ of a notebook cell that got closed.")) (lem-lsp-base/type:define-class initialize-error common-lisp:nil - ((retry :type lem-lsp-base/type:lsp-boolean :initarg :retry :accessor - initialize-error-retry :documentation - "Indicates whether the client execute the following retry logic: + ((retry :type lem-lsp-base/type:lsp-boolean :initarg :retry :accessor initialize-error-retry + :documentation "Indicates whether the client execute the following retry logic: (1) show the message provided by the ResponseError to the user (2) user selects retry or cancel (3) if user selected retry the initialize method is sent again.")) @@ -2955,8 +3056,7 @@ initialize request fails.")) common-lisp:nil ((section :type (common-lisp:or lem-lsp-base/type:lsp-string - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-string)) + (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-string)) :initarg :section :accessor did-change-configuration-registration-options-section :optional common-lisp:t))) @@ -2974,14 +3074,14 @@ initialize request fails.")) :documentation "The message type. See {@link MessageType}") (message :type lem-lsp-base/type:lsp-string :initarg :message :accessor show-message-request-params-message :documentation "The actual message.") - (actions :type (lem-lsp-base/type:lsp-array message-action-item) :initarg - :actions :accessor show-message-request-params-actions :optional common-lisp:t :documentation + (actions :type (lem-lsp-base/type:lsp-array message-action-item) :initarg :actions :accessor + show-message-request-params-actions :optional common-lisp:t :documentation "The message action items to present."))) (lem-lsp-base/type:define-class message-action-item common-lisp:nil - ((title :type lem-lsp-base/type:lsp-string :initarg :title :accessor - message-action-item-title :documentation "A short title like 'Retry', 'Open Log' etc."))) + ((title :type lem-lsp-base/type:lsp-string :initarg :title :accessor message-action-item-title + :documentation "A short title like 'Retry', 'Open Log' etc."))) (lem-lsp-base/type:define-class log-message-params common-lisp:nil @@ -3004,8 +3104,7 @@ initialize request fails.")) "The document that did change. The version number points to the version after all provided content changes have been applied.") - (content-changes :type - (lem-lsp-base/type:lsp-array text-document-content-change-event) :initarg + (content-changes :type (lem-lsp-base/type:lsp-array text-document-content-change-event) :initarg :content-changes :accessor did-change-text-document-params-content-changes :documentation "The actual content changes. The content changes describe single state changes to the document. So if there are two content changes c1 (at array index 0) and @@ -3061,21 +3160,21 @@ when the save notification was requested.")) ((range :type range :initarg :range :accessor text-edit-range :documentation "The range of the text document to be manipulated. To insert text into a document create a range where start === end.") - (new-text :type lem-lsp-base/type:lsp-string :initarg :new-text :accessor - text-edit-new-text :documentation "The string to be inserted. For delete operations use an + (new-text :type lem-lsp-base/type:lsp-string :initarg :new-text :accessor text-edit-new-text + :documentation "The string to be inserted. For delete operations use an empty string.")) (:documentation "A text edit applicable to a text document.")) (lem-lsp-base/type:define-class did-change-watched-files-params common-lisp:nil - ((changes :type (lem-lsp-base/type:lsp-array file-event) :initarg :changes - :accessor did-change-watched-files-params-changes :documentation "The actual file events.")) + ((changes :type (lem-lsp-base/type:lsp-array file-event) :initarg :changes :accessor + did-change-watched-files-params-changes :documentation "The actual file events.")) (:documentation "The watched files change notification's parameters.")) (lem-lsp-base/type:define-class did-change-watched-files-registration-options common-lisp:nil - ((watchers :type (lem-lsp-base/type:lsp-array file-system-watcher) :initarg - :watchers :accessor did-change-watched-files-registration-options-watchers :documentation + ((watchers :type (lem-lsp-base/type:lsp-array file-system-watcher) :initarg :watchers :accessor + did-change-watched-files-registration-options-watchers :documentation "The watchers to register.")) (:documentation "Describe options to be used when registered for text document change events.")) @@ -3089,8 +3188,8 @@ empty string.")) "Optional the version number of the document the diagnostics are published for. @since 3.15.0") - (diagnostics :type (lem-lsp-base/type:lsp-array diagnostic) :initarg - :diagnostics :accessor publish-diagnostics-params-diagnostics :documentation + (diagnostics :type (lem-lsp-base/type:lsp-array diagnostic) :initarg :diagnostics :accessor + publish-diagnostics-params-diagnostics :documentation "An array of diagnostic information items.")) (:documentation "The publish diagnostic notification's parameters.")) @@ -3104,8 +3203,8 @@ to send this using the client capability `textDocument.completion.contextSupport (lem-lsp-base/type:define-class completion-item common-lisp:nil - ((label :type lem-lsp-base/type:lsp-string :initarg :label :accessor - completion-item-label :documentation "The label of this completion item. + ((label :type lem-lsp-base/type:lsp-string :initarg :label :accessor completion-item-label + :documentation "The label of this completion item. The label property is also by default the text that is inserted when selecting this completion. @@ -3120,21 +3219,19 @@ be an unqualified name of the completion item.") (kind :type completion-item-kind :initarg :kind :accessor completion-item-kind :optional common-lisp:t :documentation "The kind of this completion item. Based of the kind an icon is chosen by the editor.") - (tags :type (lem-lsp-base/type:lsp-array completion-item-tag) :initarg :tags - :accessor completion-item-tags :optional common-lisp:t :since "3.15.0" :documentation + (tags :type (lem-lsp-base/type:lsp-array completion-item-tag) :initarg :tags :accessor + completion-item-tags :optional common-lisp:t :since "3.15.0" :documentation "Tags for this completion item. @since 3.15.0") - (detail :type lem-lsp-base/type:lsp-string :initarg :detail :accessor - completion-item-detail :optional common-lisp:t :documentation - "A human-readable string with additional information + (detail :type lem-lsp-base/type:lsp-string :initarg :detail :accessor completion-item-detail + :optional common-lisp:t :documentation "A human-readable string with additional information about this item, like type or symbol information.") - (documentation :type - (common-lisp:or lem-lsp-base/type:lsp-string markup-content) :initarg + (documentation :type (common-lisp:or lem-lsp-base/type:lsp-string markup-content) :initarg :documentation :accessor completion-item-documentation :optional common-lisp:t :documentation "A human-readable string that represents a doc-comment.") - (deprecated :type lem-lsp-base/type:lsp-boolean :initarg :deprecated - :accessor completion-item-deprecated :optional common-lisp:t :deprecated "Use `tags` instead." + (deprecated :type lem-lsp-base/type:lsp-boolean :initarg :deprecated :accessor + completion-item-deprecated :optional common-lisp:t :deprecated "Use `tags` instead." :documentation "Indicates if this item is deprecated. @deprecated Use `tags` instead.") (preselect :type lem-lsp-base/type:lsp-boolean :initarg :preselect :accessor @@ -3148,13 +3245,13 @@ item of those that match best is selected.") "A string that should be used when comparing this item with other items. When `falsy` the {@link CompletionItem.label label} is used.") - (filter-text :type lem-lsp-base/type:lsp-string :initarg :filter-text - :accessor completion-item-filter-text :optional common-lisp:t :documentation + (filter-text :type lem-lsp-base/type:lsp-string :initarg :filter-text :accessor + completion-item-filter-text :optional common-lisp:t :documentation "A string that should be used when filtering a set of completion items. When `falsy` the {@link CompletionItem.label label} is used.") - (insert-text :type lem-lsp-base/type:lsp-string :initarg :insert-text - :accessor completion-item-insert-text :optional common-lisp:t :documentation + (insert-text :type lem-lsp-base/type:lsp-string :initarg :insert-text :accessor + completion-item-insert-text :optional common-lisp:t :documentation "A string that should be inserted into a document when selecting this completion. When `falsy` the {@link CompletionItem.label label} is used. @@ -3204,8 +3301,8 @@ must be a prefix of the edit's replace range, that means it must be contained and starting at the same position. @since 3.16.0 additional type `InsertReplaceEdit`") - (text-edit-text :type lem-lsp-base/type:lsp-string :initarg :text-edit-text - :accessor completion-item-text-edit-text :optional common-lisp:t :since "3.17.0" :documentation + (text-edit-text :type lem-lsp-base/type:lsp-string :initarg :text-edit-text :accessor + completion-item-text-edit-text :optional common-lisp:t :since "3.17.0" :documentation "The edit text used if the completion item is part of a CompletionList and CompletionList defines an item default for the text edit range. @@ -3216,9 +3313,9 @@ If not provided and a list's default range is provided the label property is used as a text. @since 3.17.0") - (additional-text-edits :type (lem-lsp-base/type:lsp-array text-edit) - :initarg :additional-text-edits :accessor completion-item-additional-text-edits :optional - common-lisp:t :documentation + (additional-text-edits :type (lem-lsp-base/type:lsp-array text-edit) :initarg + :additional-text-edits :accessor completion-item-additional-text-edits :optional common-lisp:t + :documentation "An optional array of additional {@link TextEdit text edits} that are applied when selecting this completion. Edits must not overlap (including the same insert position) with the main {@link CompletionItem.textEdit edit} nor with themselves. @@ -3226,10 +3323,8 @@ with the main {@link CompletionItem.textEdit edit} nor with themselves. Additional text edits should be used to change text unrelated to the current cursor position (for example adding an import statement at the top of the file if the completion item will insert an unqualified type).") - (commit-characters :type - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-string) - :initarg :commit-characters :accessor completion-item-commit-characters :optional common-lisp:t + (commit-characters :type (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-string) :initarg + :commit-characters :accessor completion-item-commit-characters :optional common-lisp:t :documentation "An optional set of characters that when pressed while this completion is active will accept it first and then type that character. *Note* that all commit characters should have `length=1` and that superfluous @@ -3247,18 +3342,16 @@ proposed to complete text that is being typed.")) (lem-lsp-base/type:define-class completion-list common-lisp:nil - ((is-incomplete :type lem-lsp-base/type:lsp-boolean :initarg :is-incomplete - :accessor completion-list-is-incomplete :documentation + ((is-incomplete :type lem-lsp-base/type:lsp-boolean :initarg :is-incomplete :accessor + completion-list-is-incomplete :documentation "This list it not complete. Further typing results in recomputing this list. Recomputed lists have all their items replaced (not appended) in the incomplete completion sessions.") (item-defaults :type (lem-lsp-base/type:lsp-interface - ((commit-characters :type - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-string) - :optional common-lisp:t :since "3.17.0" :documentation "A default commit character set. + ((commit-characters :type (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-string) :optional + common-lisp:t :since "3.17.0" :documentation "A default commit character set. @since 3.17.0") (edit-range :type @@ -3294,8 +3387,8 @@ signals support for this via the `completionList.itemDefaults` capability. @since 3.17.0") - (items :type (lem-lsp-base/type:lsp-array completion-item) :initarg :items - :accessor completion-list-items :documentation "The completion items.")) + (items :type (lem-lsp-base/type:lsp-array completion-item) :initarg :items :accessor + completion-list-items :documentation "The completion items.")) (:documentation "Represents a collection of {@link CompletionItem completion items} to be presented in the editor.")) @@ -3313,8 +3406,7 @@ in the editor.")) (lem-lsp-base/type:define-class hover common-lisp:nil ((contents :type - (common-lisp:or markup-content marked-string - (lem-lsp-base/type:lsp-array marked-string)) + (common-lisp:or markup-content marked-string (lem-lsp-base/type:lsp-array marked-string)) :initarg :contents :accessor hover-contents :documentation "The hover's content") (range :type range :initarg :range :accessor hover-range :optional common-lisp:t :documentation "An optional range inside the text document that is used to @@ -3338,12 +3430,11 @@ to send this using the client capability `textDocument.signatureHelp.contextSupp (lem-lsp-base/type:define-class signature-help common-lisp:nil - ((signatures :type (lem-lsp-base/type:lsp-array signature-information) - :initarg :signatures :accessor signature-help-signatures :documentation - "One or more signatures.") - (active-signature :type lem-lsp-base/type:lsp-uinteger :initarg - :active-signature :accessor signature-help-active-signature :optional common-lisp:t - :documentation "The active signature. If omitted or the value lies outside the + ((signatures :type (lem-lsp-base/type:lsp-array signature-information) :initarg :signatures + :accessor signature-help-signatures :documentation "One or more signatures.") + (active-signature :type lem-lsp-base/type:lsp-uinteger :initarg :active-signature :accessor + signature-help-active-signature :optional common-lisp:t :documentation + "The active signature. If omitted or the value lies outside the range of `signatures` the value defaults to zero or is ignored if the `SignatureHelp` has no signatures. @@ -3352,9 +3443,9 @@ the active signature and shouldn't rely on a default value. In future version of the protocol this property might become mandatory to better express this.") - (active-parameter :type lem-lsp-base/type:lsp-uinteger :initarg - :active-parameter :accessor signature-help-active-parameter :optional common-lisp:t - :documentation "The active parameter of the active signature. If omitted or the value + (active-parameter :type lem-lsp-base/type:lsp-uinteger :initarg :active-parameter :accessor + signature-help-active-parameter :optional common-lisp:t :documentation + "The active parameter of the active signature. If omitted or the value lies outside the range of `signatures[activeSignature].parameters` defaults to 0 if the active signature has parameters. If the active signature has no parameters it is ignored. @@ -3419,8 +3510,8 @@ the background color of its range.")) (lem-lsp-base/type:define-class symbol-information (base-symbol-information) - ((deprecated :type lem-lsp-base/type:lsp-boolean :initarg :deprecated - :accessor symbol-information-deprecated :optional common-lisp:t :deprecated "Use tags instead" + ((deprecated :type lem-lsp-base/type:lsp-boolean :initarg :deprecated :accessor + symbol-information-deprecated :optional common-lisp:t :deprecated "Use tags instead" :documentation "Indicates if this symbol is deprecated. @deprecated Use tags instead") @@ -3439,22 +3530,22 @@ interfaces etc.")) (lem-lsp-base/type:define-class document-symbol common-lisp:nil - ((name :type lem-lsp-base/type:lsp-string :initarg :name :accessor - document-symbol-name :documentation + ((name :type lem-lsp-base/type:lsp-string :initarg :name :accessor document-symbol-name + :documentation "The name of this symbol. Will be displayed in the user interface and therefore must not be an empty string or a string only consisting of white spaces.") - (detail :type lem-lsp-base/type:lsp-string :initarg :detail :accessor - document-symbol-detail :optional common-lisp:t :documentation + (detail :type lem-lsp-base/type:lsp-string :initarg :detail :accessor document-symbol-detail + :optional common-lisp:t :documentation "More detail for this symbol, e.g the signature of a function.") (kind :type symbol-kind :initarg :kind :accessor document-symbol-kind :documentation "The kind of this symbol.") - (tags :type (lem-lsp-base/type:lsp-array symbol-tag) :initarg :tags - :accessor document-symbol-tags :optional common-lisp:t :since "3.16.0" :documentation + (tags :type (lem-lsp-base/type:lsp-array symbol-tag) :initarg :tags :accessor + document-symbol-tags :optional common-lisp:t :since "3.16.0" :documentation "Tags for this document symbol. @since 3.16.0") - (deprecated :type lem-lsp-base/type:lsp-boolean :initarg :deprecated - :accessor document-symbol-deprecated :optional common-lisp:t :deprecated "Use tags instead" + (deprecated :type lem-lsp-base/type:lsp-boolean :initarg :deprecated :accessor + document-symbol-deprecated :optional common-lisp:t :deprecated "Use tags instead" :documentation "Indicates if this symbol is deprecated. @deprecated Use tags instead") @@ -3466,8 +3557,8 @@ inside the symbol to reveal in the symbol in the UI.") :documentation "The range that should be selected and revealed when this symbol is being picked, e.g the name of a function. Must be contained by the `range`.") - (children :type (lem-lsp-base/type:lsp-array document-symbol) :initarg - :children :accessor document-symbol-children :optional common-lisp:t :documentation + (children :type (lem-lsp-base/type:lsp-array document-symbol) :initarg :children :accessor + document-symbol-children :optional common-lisp:t :documentation "Children of this symbol, e.g. properties of a class.")) (:documentation "Represents programming constructs like variables, classes, interfaces etc. that appear in a document. Document symbols can be hierarchical and they @@ -3492,12 +3583,12 @@ its most interesting range, e.g. the range of an identifier.")) (lem-lsp-base/type:define-class command common-lisp:nil - ((title :type lem-lsp-base/type:lsp-string :initarg :title :accessor - command-title :documentation "Title of the command, like `save`.") - (command :type lem-lsp-base/type:lsp-string :initarg :command :accessor - command-command :documentation "The identifier of the actual command handler.") - (arguments :type (lem-lsp-base/type:lsp-array lsp-any) :initarg :arguments - :accessor command-arguments :optional common-lisp:t :documentation + ((title :type lem-lsp-base/type:lsp-string :initarg :title :accessor command-title :documentation + "Title of the command, like `save`.") + (command :type lem-lsp-base/type:lsp-string :initarg :command :accessor command-command + :documentation "The identifier of the actual command handler.") + (arguments :type (lem-lsp-base/type:lsp-array lsp-any) :initarg :arguments :accessor + command-arguments :optional common-lisp:t :documentation "Arguments that the command handler should be invoked with.")) (:documentation "Represents a reference to a command. Provides a title which @@ -3507,17 +3598,17 @@ function when invoked.")) (lem-lsp-base/type:define-class code-action common-lisp:nil - ((title :type lem-lsp-base/type:lsp-string :initarg :title :accessor - code-action-title :documentation "A short, human-readable, title for this code action.") + ((title :type lem-lsp-base/type:lsp-string :initarg :title :accessor code-action-title + :documentation "A short, human-readable, title for this code action.") (kind :type code-action-kind :initarg :kind :accessor code-action-kind :optional common-lisp:t :documentation "The kind of the code action. Used to filter code actions.") - (diagnostics :type (lem-lsp-base/type:lsp-array diagnostic) :initarg - :diagnostics :accessor code-action-diagnostics :optional common-lisp:t :documentation + (diagnostics :type (lem-lsp-base/type:lsp-array diagnostic) :initarg :diagnostics :accessor + code-action-diagnostics :optional common-lisp:t :documentation "The diagnostics that this code action resolves.") - (is-preferred :type lem-lsp-base/type:lsp-boolean :initarg :is-preferred - :accessor code-action-is-preferred :optional common-lisp:t :since "3.15.0" :documentation + (is-preferred :type lem-lsp-base/type:lsp-boolean :initarg :is-preferred :accessor + code-action-is-preferred :optional common-lisp:t :since "3.15.0" :documentation "Marks this as a preferred action. Preferred actions are used by the `auto fix` command and can be targeted by keybindings. @@ -3618,8 +3709,7 @@ See also SymbolInformation. :documentation "The command this code lens represents.") (data :type lsp-any :initarg :data :accessor code-lens-data :optional common-lisp:t :documentation "A data entry field that is preserved on a code lens item between -a {@link CodeLensRequest} and a [CodeLensResolveRequest] -(#CodeLensResolveRequest)")) +a {@link CodeLensRequest} and a {@link CodeLensResolveRequest}")) (:documentation "A code lens represents a {@link Command command} that should be shown along with source text, like the number of references, a way to run tests, etc. @@ -3642,11 +3732,11 @@ reasons the creation of a code lens and resolving should be done in two stages." common-lisp:nil ((range :type range :initarg :range :accessor document-link-range :documentation "The range this link applies to.") - (target :type lem-lsp-base/type:lsp-string :initarg :target :accessor - document-link-target :optional common-lisp:t :documentation + (target :type lem-lsp-base/type:lsp-uri :initarg :target :accessor document-link-target + :optional common-lisp:t :documentation "The uri this link points to. If missing a resolve request is sent later.") - (tooltip :type lem-lsp-base/type:lsp-string :initarg :tooltip :accessor - document-link-tooltip :optional common-lisp:t :since "3.15.0" :documentation + (tooltip :type lem-lsp-base/type:lsp-string :initarg :tooltip :accessor document-link-tooltip + :optional common-lisp:t :since "3.15.0" :documentation "The tooltip text when you hover over this link. If a tooltip is provided, is will be displayed in a string that includes instructions on how to @@ -3694,6 +3784,21 @@ text document or a web site.")) common-lisp:nil (:documentation "Registration options for a {@link DocumentRangeFormattingRequest}.")) +(lem-lsp-base/type:define-class document-ranges-formatting-params + (work-done-progress-params) + ((text-document :type text-document-identifier :initarg :text-document :accessor + document-ranges-formatting-params-text-document :documentation "The document to format.") + (ranges :type (lem-lsp-base/type:lsp-array range) :initarg :ranges :accessor + document-ranges-formatting-params-ranges :documentation "The ranges to format") + (options :type formatting-options :initarg :options :accessor + document-ranges-formatting-params-options :documentation "The format options")) + (:proposed common-lisp:t) + (:since "3.18.0") + (:documentation "The parameters of a {@link DocumentRangesFormattingRequest}. + +@since 3.18.0 +@proposed")) + (lem-lsp-base/type:define-class document-on-type-formatting-params common-lisp:nil ((text-document :type text-document-identifier :initarg :text-document :accessor @@ -3724,9 +3829,8 @@ characters as well (e.g. like automatic brace completion).") rename-params-text-document :documentation "The document to rename.") (position :type position :initarg :position :accessor rename-params-position :documentation "The position at which this request was sent.") - (new-name :type lem-lsp-base/type:lsp-string :initarg :new-name :accessor - rename-params-new-name :documentation - "The new name of the symbol. If the given name is not valid the + (new-name :type lem-lsp-base/type:lsp-string :initarg :new-name :accessor rename-params-new-name + :documentation "The new name of the symbol. If the given name is not valid the request must return a {@link ResponseError} with an appropriate message set.")) (:documentation "The parameters of a {@link RenameRequest}.")) @@ -3744,8 +3848,8 @@ appropriate message set.")) (work-done-progress-params) ((command :type lem-lsp-base/type:lsp-string :initarg :command :accessor execute-command-params-command :documentation "The identifier of the actual command handler.") - (arguments :type (lem-lsp-base/type:lsp-array lsp-any) :initarg :arguments - :accessor execute-command-params-arguments :optional common-lisp:t :documentation + (arguments :type (lem-lsp-base/type:lsp-array lsp-any) :initarg :arguments :accessor + execute-command-params-arguments :optional common-lisp:t :documentation "Arguments that the command should be invoked with.")) (:documentation "The parameters of a {@link ExecuteCommandRequest}.")) @@ -3763,20 +3867,20 @@ presented in the user interface for example on an undo stack to undo the workspace edit.") (edit :type workspace-edit :initarg :edit :accessor apply-workspace-edit-params-edit :documentation "The edits to apply.")) - (:documentation "The parameters passed via a apply workspace edit request.")) + (:documentation "The parameters passed via an apply workspace edit request.")) (lem-lsp-base/type:define-class apply-workspace-edit-result common-lisp:nil ((applied :type lem-lsp-base/type:lsp-boolean :initarg :applied :accessor apply-workspace-edit-result-applied :documentation "Indicates whether the edit was applied or not.") - (failure-reason :type lem-lsp-base/type:lsp-string :initarg :failure-reason - :accessor apply-workspace-edit-result-failure-reason :optional common-lisp:t :documentation + (failure-reason :type lem-lsp-base/type:lsp-string :initarg :failure-reason :accessor + apply-workspace-edit-result-failure-reason :optional common-lisp:t :documentation "An optional textual description for why the edit was not applied. This may be used by the server for diagnostic logging or to provide a suitable error for a request that triggered the edit.") - (failed-change :type lem-lsp-base/type:lsp-uinteger :initarg :failed-change - :accessor apply-workspace-edit-result-failed-change :optional common-lisp:t :documentation + (failed-change :type lem-lsp-base/type:lsp-uinteger :initarg :failed-change :accessor + apply-workspace-edit-result-failed-change :optional common-lisp:t :documentation "Depending on the client's failure handling strategy `failedChange` might contain the index of the change that failed. This property is only available if the client signals a `failureHandlingStrategy` in its client capabilities.")) @@ -3795,8 +3899,8 @@ if the client signals a `failureHandlingStrategy` in its client capabilities.")) the kind of operation being performed. Examples: \"Indexing\" or \"Linking dependencies\".") - (cancellable :type lem-lsp-base/type:lsp-boolean :initarg :cancellable - :accessor work-done-progress-begin-cancellable :optional common-lisp:t :documentation + (cancellable :type lem-lsp-base/type:lsp-boolean :initarg :cancellable :accessor + work-done-progress-begin-cancellable :optional common-lisp:t :documentation "Controls if a cancel button should show to allow the user to cancel the long running operation. Clients that don't support cancellation are allowed to ignore the setting.") @@ -3807,8 +3911,8 @@ complementary information to the `title`. Examples: \"3/25 files\", \"project/src/module2\", \"node_modules/some_dep\". If unset, the previous progress message (if any) is still valid.") - (percentage :type lem-lsp-base/type:lsp-uinteger :initarg :percentage - :accessor work-done-progress-begin-percentage :optional common-lisp:t :documentation + (percentage :type lem-lsp-base/type:lsp-uinteger :initarg :percentage :accessor + work-done-progress-begin-percentage :optional common-lisp:t :documentation "Optional progress percentage to display (value 100 is considered 100%). If not provided infinite progress is assumed and clients are allowed to ignore the `percentage` value in subsequent in report notifications. @@ -3820,8 +3924,8 @@ that are not following this rule. The value range is [0, 100]."))) common-lisp:nil ((kind :type (lem-lsp-base/type:lsp-string "report") :initarg :kind :accessor work-done-progress-report-kind) - (cancellable :type lem-lsp-base/type:lsp-boolean :initarg :cancellable - :accessor work-done-progress-report-cancellable :optional common-lisp:t :documentation + (cancellable :type lem-lsp-base/type:lsp-boolean :initarg :cancellable :accessor + work-done-progress-report-cancellable :optional common-lisp:t :documentation "Controls enablement state of a cancel button. Clients that don't support cancellation or don't support controlling the button's @@ -3833,8 +3937,8 @@ complementary information to the `title`. Examples: \"3/25 files\", \"project/src/module2\", \"node_modules/some_dep\". If unset, the previous progress message (if any) is still valid.") - (percentage :type lem-lsp-base/type:lsp-uinteger :initarg :percentage - :accessor work-done-progress-report-percentage :optional common-lisp:t :documentation + (percentage :type lem-lsp-base/type:lsp-uinteger :initarg :percentage :accessor + work-done-progress-report-percentage :optional common-lisp:t :documentation "Optional progress percentage to display (value 100 is considered 100%). If not provided infinite progress is assumed and clients are allowed to ignore the `percentage` value in subsequent in report notifications. @@ -3859,15 +3963,13 @@ of the operation."))) common-lisp:nil ((message :type lem-lsp-base/type:lsp-string :initarg :message :accessor log-trace-params-message) - (verbose :type lem-lsp-base/type:lsp-string :initarg :verbose :accessor - log-trace-params-verbose :optional common-lisp:t))) + (verbose :type lem-lsp-base/type:lsp-string :initarg :verbose :accessor log-trace-params-verbose + :optional common-lisp:t))) (lem-lsp-base/type:define-class cancel-params common-lisp:nil - ((id :type - (common-lisp:or lem-lsp-base/type:lsp-integer - lem-lsp-base/type:lsp-string) - :initarg :id :accessor cancel-params-id :documentation "The request id to cancel."))) + ((id :type (common-lisp:or lem-lsp-base/type:lsp-integer lem-lsp-base/type:lsp-string) :initarg + :id :accessor cancel-params-id :documentation "The request id to cancel."))) (lem-lsp-base/type:define-class progress-params common-lisp:nil @@ -3907,9 +4009,8 @@ the client."))) Used as the underlined span for mouse interaction. Defaults to the word range at the definition position.") - (target-uri :type lem-lsp-base/type:lsp-document-uri :initarg :target-uri - :accessor location-link-target-uri :documentation - "The target resource identifier of this link.") + (target-uri :type lem-lsp-base/type:lsp-document-uri :initarg :target-uri :accessor + location-link-target-uri :documentation "The target resource identifier of this link.") (target-range :type range :initarg :target-range :accessor location-link-target-range :documentation "The full target range of this link. If the target for example is a symbol then target range is the @@ -3947,8 +4048,8 @@ For example: (lem-lsp-base/type:define-class static-registration-options common-lisp:nil - ((id :type lem-lsp-base/type:lsp-string :initarg :id :accessor - static-registration-options-id :optional common-lisp:t :documentation + ((id :type lem-lsp-base/type:lsp-string :initarg :id :accessor static-registration-options-id + :optional common-lisp:t :documentation "The id used to register the request. The id can be used to deregister the request again. See also Registration#id.")) (:documentation "Static registration options to be returned in the initialize @@ -3960,17 +4061,16 @@ request.")) (lem-lsp-base/type:define-class workspace-folders-change-event common-lisp:nil - ((added :type (lem-lsp-base/type:lsp-array workspace-folder) :initarg :added - :accessor workspace-folders-change-event-added :documentation - "The array of added workspace folders") - (removed :type (lem-lsp-base/type:lsp-array workspace-folder) :initarg - :removed :accessor workspace-folders-change-event-removed :documentation + ((added :type (lem-lsp-base/type:lsp-array workspace-folder) :initarg :added :accessor + workspace-folders-change-event-added :documentation "The array of added workspace folders") + (removed :type (lem-lsp-base/type:lsp-array workspace-folder) :initarg :removed :accessor + workspace-folders-change-event-removed :documentation "The array of the removed workspace folders")) (:documentation "The workspace folder change event.")) (lem-lsp-base/type:define-class configuration-item common-lisp:nil - ((scope-uri :type lem-lsp-base/type:lsp-string :initarg :scope-uri :accessor + ((scope-uri :type lem-lsp-base/type:lsp-uri :initarg :scope-uri :accessor configuration-item-scope-uri :optional common-lisp:t :documentation "The scope to get the configuration section for.") (section :type lem-lsp-base/type:lsp-string :initarg :section :accessor @@ -3985,14 +4085,14 @@ request.")) (lem-lsp-base/type:define-class color common-lisp:nil - ((red :type lem-lsp-base/type:lsp-decimal :initarg :red :accessor color-red - :documentation "The red component of this color in the range [0-1].") - (green :type lem-lsp-base/type:lsp-decimal :initarg :green :accessor - color-green :documentation "The green component of this color in the range [0-1].") - (blue :type lem-lsp-base/type:lsp-decimal :initarg :blue :accessor - color-blue :documentation "The blue component of this color in the range [0-1].") - (alpha :type lem-lsp-base/type:lsp-decimal :initarg :alpha :accessor - color-alpha :documentation "The alpha component of this color in the range [0-1].")) + ((red :type lem-lsp-base/type:lsp-decimal :initarg :red :accessor color-red :documentation + "The red component of this color in the range [0-1].") + (green :type lem-lsp-base/type:lsp-decimal :initarg :green :accessor color-green :documentation + "The green component of this color in the range [0-1].") + (blue :type lem-lsp-base/type:lsp-decimal :initarg :blue :accessor color-blue :documentation + "The blue component of this color in the range [0-1].") + (alpha :type lem-lsp-base/type:lsp-decimal :initarg :alpha :accessor color-alpha :documentation + "The alpha component of this color in the range [0-1].")) (:documentation "Represents a color in RGBA space.")) (lem-lsp-base/type:define-class document-color-options @@ -4009,14 +4109,13 @@ request.")) (lem-lsp-base/type:define-class position common-lisp:nil - ((line :type lem-lsp-base/type:lsp-uinteger :initarg :line :accessor - position-line :documentation "Line position in a document (zero-based). + ((line :type lem-lsp-base/type:lsp-uinteger :initarg :line :accessor position-line :documentation + "Line position in a document (zero-based). If a line number is greater than the number of lines in a document, it defaults back to the number of lines in the document. If a line number is negative, it defaults to 0.") - (character :type lem-lsp-base/type:lsp-uinteger :initarg :character - :accessor position-character :documentation - "Character offset on a line in a document (zero-based). + (character :type lem-lsp-base/type:lsp-uinteger :initarg :character :accessor position-character + :documentation "Character offset on a line in a document (zero-based). The meaning of this offset is determined by the negotiated `PositionEncodingKind`. @@ -4031,14 +4130,14 @@ character `a` is 0, the character offset of `𐐀` is 1 and the character offset of b is 3 since `𐐀` is represented using two code units in UTF-16. Since 3.17 clients and servers can agree on a different string encoding representation (e.g. UTF-8). The client announces it's supported encoding -via the client capability [`general.positionEncodings`](#clientCapabilities). +via the client capability [`general.positionEncodings`](https://microsoft.github.io/language-server-protocol/specifications/specification-current/#clientCapabilities). The value is an array of position encodings the client supports, with decreasing preference (e.g. the encoding at index `0` is the most preferred one). To stay backwards compatible the only mandatory encoding is UTF-16 represented via the string `utf-16`. The server can pick one of the encodings offered by the client and signals that encoding back to the client via the initialize result's property -[`capabilities.positionEncoding`](#serverCapabilities). If the string value +[`capabilities.positionEncoding`](https://microsoft.github.io/language-server-protocol/specifications/specification-current/#serverCapabilities). If the string value `utf-16` is missing from the client's capability `general.positionEncodings` servers can safely assume that the client supports UTF-16. If the server omits the position encoding in its initialize result the encoding defaults @@ -4077,9 +4176,8 @@ of a document.") (full :type (common-lisp:or lem-lsp-base/type:lsp-boolean (lem-lsp-base/type:lsp-interface - ((delta :type lem-lsp-base/type:lsp-boolean :optional - common-lisp:t :documentation - "The server supports deltas for full documents.")))) + ((delta :type lem-lsp-base/type:lsp-boolean :optional common-lisp:t + :documentation "The server supports deltas for full documents.")))) :initarg :full :accessor semantic-tokens-options-full :optional common-lisp:t :documentation "Server supports providing semantic tokens for a full document.")) (:since "3.16.0") @@ -4087,14 +4185,12 @@ of a document.") (lem-lsp-base/type:define-class semantic-tokens-edit common-lisp:nil - ((start :type lem-lsp-base/type:lsp-uinteger :initarg :start :accessor - semantic-tokens-edit-start :documentation "The start offset of the edit.") - (delete-count :type lem-lsp-base/type:lsp-uinteger :initarg :delete-count - :accessor semantic-tokens-edit-delete-count :documentation "The count of elements to remove.") - (data :type - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-uinteger) - :initarg :data :accessor semantic-tokens-edit-data :optional common-lisp:t :documentation + ((start :type lem-lsp-base/type:lsp-uinteger :initarg :start :accessor semantic-tokens-edit-start + :documentation "The start offset of the edit.") + (delete-count :type lem-lsp-base/type:lsp-uinteger :initarg :delete-count :accessor + semantic-tokens-edit-delete-count :documentation "The count of elements to remove.") + (data :type (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-uinteger) :initarg :data + :accessor semantic-tokens-edit-data :optional common-lisp:t :documentation "The elements to insert.")) (:since "3.16.0") (:documentation "@since 3.16.0")) @@ -4105,8 +4201,7 @@ of a document.") (lem-lsp-base/type:define-class file-create common-lisp:nil - ((uri :type lem-lsp-base/type:lsp-string :initarg :uri :accessor - file-create-uri :documentation + ((uri :type lem-lsp-base/type:lsp-string :initarg :uri :accessor file-create-uri :documentation "A file:// URI for the location of the file/folder being created.")) (:since "3.16.0") (:documentation "Represents information on a file/folder create. @@ -4117,9 +4212,7 @@ of a document.") common-lisp:nil ((text-document :type optional-versioned-text-document-identifier :initarg :text-document :accessor text-document-edit-text-document :documentation "The text document to change.") - (edits :type - (lem-lsp-base/type:lsp-array - (common-lisp:or text-edit annotated-text-edit)) + (edits :type (lem-lsp-base/type:lsp-array (common-lisp:or text-edit annotated-text-edit)) :initarg :edits :accessor text-document-edit-edits :since "3.16.0 - support for AnnotatedTextEdit. This is guarded using a client capability." @@ -4135,48 +4228,47 @@ kind of ordering. However the edits must be non overlapping.")) (lem-lsp-base/type:define-class create-file (resource-operation) - ((kind :type (lem-lsp-base/type:lsp-string "create") :initarg :kind :accessor - create-file-kind :documentation "A create") - (uri :type lem-lsp-base/type:lsp-document-uri :initarg :uri :accessor - create-file-uri :documentation "The resource to create.") + ((kind :type (lem-lsp-base/type:lsp-string "create") :initarg :kind :accessor create-file-kind + :documentation "A create") + (uri :type lem-lsp-base/type:lsp-document-uri :initarg :uri :accessor create-file-uri + :documentation "The resource to create.") (options :type create-file-options :initarg :options :accessor create-file-options :optional common-lisp:t :documentation "Additional options")) (:documentation "Create file operation.")) (lem-lsp-base/type:define-class rename-file (resource-operation) - ((kind :type (lem-lsp-base/type:lsp-string "rename") :initarg :kind :accessor - rename-file-kind :documentation "A rename") - (old-uri :type lem-lsp-base/type:lsp-document-uri :initarg :old-uri - :accessor rename-file-old-uri :documentation "The old (existing) location.") - (new-uri :type lem-lsp-base/type:lsp-document-uri :initarg :new-uri - :accessor rename-file-new-uri :documentation "The new location.") + ((kind :type (lem-lsp-base/type:lsp-string "rename") :initarg :kind :accessor rename-file-kind + :documentation "A rename") + (old-uri :type lem-lsp-base/type:lsp-document-uri :initarg :old-uri :accessor + rename-file-old-uri :documentation "The old (existing) location.") + (new-uri :type lem-lsp-base/type:lsp-document-uri :initarg :new-uri :accessor + rename-file-new-uri :documentation "The new location.") (options :type rename-file-options :initarg :options :accessor rename-file-options :optional common-lisp:t :documentation "Rename options.")) (:documentation "Rename file operation")) (lem-lsp-base/type:define-class delete-file (resource-operation) - ((kind :type (lem-lsp-base/type:lsp-string "delete") :initarg :kind :accessor - delete-file-kind :documentation "A delete") - (uri :type lem-lsp-base/type:lsp-document-uri :initarg :uri :accessor - delete-file-uri :documentation "The file to delete.") + ((kind :type (lem-lsp-base/type:lsp-string "delete") :initarg :kind :accessor delete-file-kind + :documentation "A delete") + (uri :type lem-lsp-base/type:lsp-document-uri :initarg :uri :accessor delete-file-uri + :documentation "The file to delete.") (options :type delete-file-options :initarg :options :accessor delete-file-options :optional common-lisp:t :documentation "Delete options.")) (:documentation "Delete file operation")) (lem-lsp-base/type:define-class change-annotation common-lisp:nil - ((label :type lem-lsp-base/type:lsp-string :initarg :label :accessor - change-annotation-label :documentation - "A human-readable string describing the actual change. The string + ((label :type lem-lsp-base/type:lsp-string :initarg :label :accessor change-annotation-label + :documentation "A human-readable string describing the actual change. The string is rendered prominent in the user interface.") - (needs-confirmation :type lem-lsp-base/type:lsp-boolean :initarg - :needs-confirmation :accessor change-annotation-needs-confirmation :optional common-lisp:t - :documentation "A flag which indicates that user confirmation is needed + (needs-confirmation :type lem-lsp-base/type:lsp-boolean :initarg :needs-confirmation :accessor + change-annotation-needs-confirmation :optional common-lisp:t :documentation + "A flag which indicates that user confirmation is needed before applying the change.") - (description :type lem-lsp-base/type:lsp-string :initarg :description - :accessor change-annotation-description :optional common-lisp:t :documentation + (description :type lem-lsp-base/type:lsp-string :initarg :description :accessor + change-annotation-description :optional common-lisp:t :documentation "A human-readable string which is rendered less prominent in the user interface.")) (:since "3.16.0") @@ -4199,12 +4291,10 @@ the server is interested in receiving. (lem-lsp-base/type:define-class file-rename common-lisp:nil - ((old-uri :type lem-lsp-base/type:lsp-string :initarg :old-uri :accessor - file-rename-old-uri :documentation - "A file:// URI for the original location of the file/folder being renamed.") - (new-uri :type lem-lsp-base/type:lsp-string :initarg :new-uri :accessor - file-rename-new-uri :documentation - "A file:// URI for the new location of the file/folder being renamed.")) + ((old-uri :type lem-lsp-base/type:lsp-string :initarg :old-uri :accessor file-rename-old-uri + :documentation "A file:// URI for the original location of the file/folder being renamed.") + (new-uri :type lem-lsp-base/type:lsp-string :initarg :new-uri :accessor file-rename-new-uri + :documentation "A file:// URI for the new location of the file/folder being renamed.")) (:since "3.16.0") (:documentation "Represents information on a file/folder rename. @@ -4212,8 +4302,7 @@ the server is interested in receiving. (lem-lsp-base/type:define-class file-delete common-lisp:nil - ((uri :type lem-lsp-base/type:lsp-string :initarg :uri :accessor - file-delete-uri :documentation + ((uri :type lem-lsp-base/type:lsp-string :initarg :uri :accessor file-delete-uri :documentation "A file:// URI for the location of the file/folder being deleted.")) (:since "3.16.0") (:documentation "Represents information on a file/folder delete. @@ -4248,8 +4337,8 @@ Typically the end position of the range denotes the line where the inline values common-lisp:nil ((range :type range :initarg :range :accessor inline-value-text-range :documentation "The document range for which the inline value applies.") - (text :type lem-lsp-base/type:lsp-string :initarg :text :accessor - inline-value-text-text :documentation "The text of the inline value.")) + (text :type lem-lsp-base/type:lsp-string :initarg :text :accessor inline-value-text-text + :documentation "The text of the inline value.")) (:since "3.17.0") (:documentation "Provide inline value as text. @@ -4260,12 +4349,12 @@ Typically the end position of the range denotes the line where the inline values ((range :type range :initarg :range :accessor inline-value-variable-lookup-range :documentation "The document range for which the inline value applies. The range is used to extract the variable name from the underlying document.") - (variable-name :type lem-lsp-base/type:lsp-string :initarg :variable-name - :accessor inline-value-variable-lookup-variable-name :optional common-lisp:t :documentation + (variable-name :type lem-lsp-base/type:lsp-string :initarg :variable-name :accessor + inline-value-variable-lookup-variable-name :optional common-lisp:t :documentation "If specified the name of the variable to look up.") - (case-sensitive-lookup :type lem-lsp-base/type:lsp-boolean :initarg - :case-sensitive-lookup :accessor inline-value-variable-lookup-case-sensitive-lookup - :documentation "How to perform the lookup.")) + (case-sensitive-lookup :type lem-lsp-base/type:lsp-boolean :initarg :case-sensitive-lookup + :accessor inline-value-variable-lookup-case-sensitive-lookup :documentation + "How to perform the lookup.")) (:since "3.17.0") (:documentation "Provide inline value through a variable lookup. If only a range is specified, the variable name will be extracted from the underlying document. @@ -4278,8 +4367,8 @@ An optional variable name can be used to override the extracted name. ((range :type range :initarg :range :accessor inline-value-evaluatable-expression-range :documentation "The document range for which the inline value applies. The range is used to extract the evaluatable expression from the underlying document.") - (expression :type lem-lsp-base/type:lsp-string :initarg :expression - :accessor inline-value-evaluatable-expression-expression :optional common-lisp:t :documentation + (expression :type lem-lsp-base/type:lsp-string :initarg :expression :accessor + inline-value-evaluatable-expression-expression :optional common-lisp:t :documentation "If specified the expression overrides the extracted expression.")) (:since "3.17.0") (:documentation "Provide an inline value through an expression evaluation. @@ -4298,11 +4387,11 @@ An optional expression can be used to override the extracted expression. (lem-lsp-base/type:define-class inlay-hint-label-part common-lisp:nil - ((value :type lem-lsp-base/type:lsp-string :initarg :value :accessor - inlay-hint-label-part-value :documentation "The value of this label part.") - (tooltip :type (common-lisp:or lem-lsp-base/type:lsp-string markup-content) - :initarg :tooltip :accessor inlay-hint-label-part-tooltip :optional common-lisp:t - :documentation "The tooltip text when you hover over this label part. Depending on + ((value :type lem-lsp-base/type:lsp-string :initarg :value :accessor inlay-hint-label-part-value + :documentation "The value of this label part.") + (tooltip :type (common-lisp:or lem-lsp-base/type:lsp-string markup-content) :initarg :tooltip + :accessor inlay-hint-label-part-tooltip :optional common-lisp:t :documentation + "The tooltip text when you hover over this label part. Depending on the client capability `inlayHint.resolveSupport` clients might resolve this property late using the resolve request.") (location :type location :initarg :location :accessor inlay-hint-label-part-location :optional @@ -4332,8 +4421,8 @@ of inlay hints. common-lisp:nil ((kind :type markup-kind :initarg :kind :accessor markup-content-kind :documentation "The type of the Markup") - (value :type lem-lsp-base/type:lsp-string :initarg :value :accessor - markup-content-value :documentation "The content itself")) + (value :type lem-lsp-base/type:lsp-string :initarg :value :accessor markup-content-value + :documentation "The content itself")) (:documentation "A `MarkupContent` literal represents a string value which content is interpreted base on its kind flag. Currently the protocol supports `plaintext` and `markdown` as markup kinds. @@ -4360,9 +4449,9 @@ remove HTML from the markdown to avoid script execution.")) (lem-lsp-base/type:define-class inlay-hint-options (work-done-progress-options) - ((resolve-provider :type lem-lsp-base/type:lsp-boolean :initarg - :resolve-provider :accessor inlay-hint-options-resolve-provider :optional common-lisp:t - :documentation "The server provides support to resolve additional + ((resolve-provider :type lem-lsp-base/type:lsp-boolean :initarg :resolve-provider :accessor + inlay-hint-options-resolve-provider :optional common-lisp:t :documentation + "The server provides support to resolve additional information for an inlay hint item.")) (:since "3.17.0") (:documentation "Inlay hint options used during static registration. @@ -4372,7 +4461,7 @@ information for an inlay hint item.")) (lem-lsp-base/type:define-class related-full-document-diagnostic-report (full-document-diagnostic-report) ((related-documents :type - (lem-lsp-base/type:lsp-map document-uri + (lem-lsp-base/type:lsp-map lem-lsp-base/type:lsp-document-uri (common-lisp:or full-document-diagnostic-report unchanged-document-diagnostic-report)) :initarg :related-documents :accessor related-full-document-diagnostic-report-related-documents :optional common-lisp:t :since "3.17.0" :documentation @@ -4391,7 +4480,7 @@ a.cpp and result in errors in a header file b.hpp. (lem-lsp-base/type:define-class related-unchanged-document-diagnostic-report (unchanged-document-diagnostic-report) ((related-documents :type - (lem-lsp-base/type:lsp-map document-uri + (lem-lsp-base/type:lsp-map lem-lsp-base/type:lsp-document-uri (common-lisp:or full-document-diagnostic-report unchanged-document-diagnostic-report)) :initarg :related-documents :accessor related-unchanged-document-diagnostic-report-related-documents :optional common-lisp:t :since @@ -4416,8 +4505,8 @@ a.cpp and result in errors in a header file b.hpp. "An optional result id. If provided it will be sent on the next diagnostic request for the same document.") - (items :type (lem-lsp-base/type:lsp-array diagnostic) :initarg :items - :accessor full-document-diagnostic-report-items :documentation "The actual items.")) + (items :type (lem-lsp-base/type:lsp-array diagnostic) :initarg :items :accessor + full-document-diagnostic-report-items :documentation "The actual items.")) (:since "3.17.0") (:documentation "A diagnostic report with a full set of problems. @@ -4425,8 +4514,8 @@ same document.") (lem-lsp-base/type:define-class unchanged-document-diagnostic-report common-lisp:nil - ((kind :type (lem-lsp-base/type:lsp-string "unchanged") :initarg :kind - :accessor unchanged-document-diagnostic-report-kind :documentation + ((kind :type (lem-lsp-base/type:lsp-string "unchanged") :initarg :kind :accessor + unchanged-document-diagnostic-report-kind :documentation "A document diagnostic report indicating no changes to the last result. A server can only return `unchanged` if result ids are @@ -4443,18 +4532,18 @@ report is still accurate. (lem-lsp-base/type:define-class diagnostic-options (work-done-progress-options) - ((identifier :type lem-lsp-base/type:lsp-string :initarg :identifier - :accessor diagnostic-options-identifier :optional common-lisp:t :documentation + ((identifier :type lem-lsp-base/type:lsp-string :initarg :identifier :accessor + diagnostic-options-identifier :optional common-lisp:t :documentation "An optional identifier under which the diagnostics are managed by the client.") - (inter-file-dependencies :type lem-lsp-base/type:lsp-boolean :initarg - :inter-file-dependencies :accessor diagnostic-options-inter-file-dependencies :documentation + (inter-file-dependencies :type lem-lsp-base/type:lsp-boolean :initarg :inter-file-dependencies + :accessor diagnostic-options-inter-file-dependencies :documentation "Whether the language has inter file dependencies meaning that editing code in one file can result in a different diagnostic set in another file. Inter file dependencies are common for most programming languages and typically uncommon for linters.") - (workspace-diagnostics :type lem-lsp-base/type:lsp-boolean :initarg - :workspace-diagnostics :accessor diagnostic-options-workspace-diagnostics :documentation + (workspace-diagnostics :type lem-lsp-base/type:lsp-boolean :initarg :workspace-diagnostics + :accessor diagnostic-options-workspace-diagnostics :documentation "The server provides support for workspace diagnostics as well.")) (:since "3.17.0") (:documentation "Diagnostic options. @@ -4463,11 +4552,11 @@ most programming languages and typically uncommon for linters.") (lem-lsp-base/type:define-class previous-result-id common-lisp:nil - ((uri :type lem-lsp-base/type:lsp-document-uri :initarg :uri :accessor - previous-result-id-uri :documentation "The URI for which the client knowns a + ((uri :type lem-lsp-base/type:lsp-document-uri :initarg :uri :accessor previous-result-id-uri + :documentation "The URI for which the client knowns a result id.") - (value :type lem-lsp-base/type:lsp-string :initarg :value :accessor - previous-result-id-value :documentation "The value of the previous result id.")) + (value :type lem-lsp-base/type:lsp-string :initarg :value :accessor previous-result-id-value + :documentation "The value of the previous result id.")) (:since "3.17.0") (:documentation "A previous result id in a workspace pull request. @@ -4475,10 +4564,10 @@ result id.") (lem-lsp-base/type:define-class notebook-document common-lisp:nil - ((uri :type lem-lsp-base/type:lsp-uri :initarg :uri :accessor - notebook-document-uri :documentation "The notebook document's uri.") - (notebook-type :type lem-lsp-base/type:lsp-string :initarg :notebook-type - :accessor notebook-document-notebook-type :documentation "The type of the notebook.") + ((uri :type lem-lsp-base/type:lsp-uri :initarg :uri :accessor notebook-document-uri + :documentation "The notebook document's uri.") + (notebook-type :type lem-lsp-base/type:lsp-string :initarg :notebook-type :accessor + notebook-document-notebook-type :documentation "The type of the notebook.") (version :type lem-lsp-base/type:lsp-integer :initarg :version :accessor notebook-document-version :documentation "The version number of this document (it will increase after each @@ -4488,8 +4577,8 @@ change, including undo/redo).") document. Note: should always be an object literal (e.g. LSPObject)") - (cells :type (lem-lsp-base/type:lsp-array notebook-cell) :initarg :cells - :accessor notebook-document-cells :documentation "The cells of a notebook.")) + (cells :type (lem-lsp-base/type:lsp-array notebook-cell) :initarg :cells :accessor + notebook-document-cells :documentation "The cells of a notebook.")) (:since "3.17.0") (:documentation "A notebook document. @@ -4497,17 +4586,16 @@ Note: should always be an object literal (e.g. LSPObject)") (lem-lsp-base/type:define-class text-document-item common-lisp:nil - ((uri :type lem-lsp-base/type:lsp-document-uri :initarg :uri :accessor - text-document-item-uri :documentation "The text document's uri.") - (language-id :type lem-lsp-base/type:lsp-string :initarg :language-id - :accessor text-document-item-language-id :documentation - "The text document's language identifier.") + ((uri :type lem-lsp-base/type:lsp-document-uri :initarg :uri :accessor text-document-item-uri + :documentation "The text document's uri.") + (language-id :type lem-lsp-base/type:lsp-string :initarg :language-id :accessor + text-document-item-language-id :documentation "The text document's language identifier.") (version :type lem-lsp-base/type:lsp-integer :initarg :version :accessor text-document-item-version :documentation "The version number of this document (it will increase after each change, including undo/redo).") - (text :type lem-lsp-base/type:lsp-string :initarg :text :accessor - text-document-item-text :documentation "The content of the opened text document.")) + (text :type lem-lsp-base/type:lsp-string :initarg :text :accessor text-document-item-text + :documentation "The content of the opened text document.")) (:documentation "An item to transfer a text document from the client to the server.")) @@ -4534,22 +4622,20 @@ Note: should always be an object literal (e.g. LSPObject)") ((structure :type (lem-lsp-base/type:lsp-interface ((array :type notebook-cell-array-change :documentation "The change to the cell array.") - (did-open :type (lem-lsp-base/type:lsp-array text-document-item) - :optional common-lisp:t :documentation "Additional opened cell text documents.") - (did-close :type - (lem-lsp-base/type:lsp-array text-document-identifier) :optional + (did-open :type (lem-lsp-base/type:lsp-array text-document-item) :optional common-lisp:t + :documentation "Additional opened cell text documents.") + (did-close :type (lem-lsp-base/type:lsp-array text-document-identifier) :optional common-lisp:t :documentation "Additional closed cell text documents."))) :optional common-lisp:t :documentation "Changes to the cell structure to add or remove cells.") - (data :type (lem-lsp-base/type:lsp-array notebook-cell) :optional - common-lisp:t :documentation "Changes to notebook cells properties like its + (data :type (lem-lsp-base/type:lsp-array notebook-cell) :optional common-lisp:t + :documentation "Changes to notebook cells properties like its kind, execution summary or metadata.") (text-content :type (lem-lsp-base/type:lsp-array (lem-lsp-base/type:lsp-interface ((document :type versioned-text-document-identifier) - (changes :type - (lem-lsp-base/type:lsp-array text-document-content-change-event))))) + (changes :type (lem-lsp-base/type:lsp-array text-document-content-change-event))))) :optional common-lisp:t :documentation "Changes to the text content of notebook cells."))) :initarg :cells :accessor notebook-document-change-event-cells :optional common-lisp:t :documentation "Changes to cells")) @@ -4560,42 +4646,83 @@ kind, execution summary or metadata.") (lem-lsp-base/type:define-class notebook-document-identifier common-lisp:nil - ((uri :type lem-lsp-base/type:lsp-uri :initarg :uri :accessor - notebook-document-identifier-uri :documentation "The notebook document's uri.")) + ((uri :type lem-lsp-base/type:lsp-uri :initarg :uri :accessor notebook-document-identifier-uri + :documentation "The notebook document's uri.")) (:since "3.17.0") (:documentation "A literal to identify a notebook document in the client. @since 3.17.0")) +(lem-lsp-base/type:define-class inline-completion-context + common-lisp:nil + ((trigger-kind :type inline-completion-trigger-kind :initarg :trigger-kind :accessor + inline-completion-context-trigger-kind :documentation + "Describes how the inline completion was triggered.") + (selected-completion-info :type selected-completion-info :initarg :selected-completion-info + :accessor inline-completion-context-selected-completion-info :optional common-lisp:t + :documentation + "Provides information about the currently selected item in the autocomplete widget if it is visible.")) + (:proposed common-lisp:t) + (:since "3.18.0") + (:documentation + "Provides information about the context in which an inline completion was requested. + +@since 3.18.0 +@proposed")) + +(lem-lsp-base/type:define-class string-value + common-lisp:nil + ((kind :type (lem-lsp-base/type:lsp-string "snippet") :initarg :kind :accessor string-value-kind + :documentation "The kind of string value.") + (value :type lem-lsp-base/type:lsp-string :initarg :value :accessor string-value-value + :documentation "The snippet string.")) + (:proposed common-lisp:t) + (:since "3.18.0") + (:documentation "A string value used as a snippet is a template which allows to insert text +and to control the editor cursor when insertion happens. + +A snippet can define tab stops and placeholders with `$1`, `$2` +and `${3:foo}`. `$0` defines the final tab stop, it defaults to +the end of the snippet. Variables are defined with `$name` and +`${name:default value}`. + +@since 3.18.0 +@proposed")) + +(lem-lsp-base/type:define-class inline-completion-options + (work-done-progress-options) + common-lisp:nil + (:proposed common-lisp:t) + (:since "3.18.0") + (:documentation "Inline completion options used during static registration. + +@since 3.18.0 +@proposed")) + (lem-lsp-base/type:define-class registration common-lisp:nil - ((id :type lem-lsp-base/type:lsp-string :initarg :id :accessor - registration-id :documentation + ((id :type lem-lsp-base/type:lsp-string :initarg :id :accessor registration-id :documentation "The id used to register the request. The id can be used to deregister the request again.") - (method :type lem-lsp-base/type:lsp-string :initarg :method :accessor - registration-method :documentation "The method / capability to register for.") + (method :type lem-lsp-base/type:lsp-string :initarg :method :accessor registration-method + :documentation "The method / capability to register for.") (register-options :type lsp-any :initarg :register-options :accessor registration-register-options :optional common-lisp:t :documentation "Options necessary for the registration.")) - (:documentation - "General parameters to to register for an notification or to register a provider.")) + (:documentation "General parameters to register for a notification or to register a provider.")) (lem-lsp-base/type:define-class unregistration common-lisp:nil - ((id :type lem-lsp-base/type:lsp-string :initarg :id :accessor - unregistration-id :documentation + ((id :type lem-lsp-base/type:lsp-string :initarg :id :accessor unregistration-id :documentation "The id used to unregister the request or notification. Usually an id provided during the register request.") - (method :type lem-lsp-base/type:lsp-string :initarg :method :accessor - unregistration-method :documentation "The method to unregister for.")) + (method :type lem-lsp-base/type:lsp-string :initarg :method :accessor unregistration-method + :documentation "The method to unregister for.")) (:documentation "General parameters to unregister a request or notification.")) (lem-lsp-base/type:define-class _initialize-params (work-done-progress-params) - ((process-id :type - (common-lisp:or lem-lsp-base/type:lsp-integer - lem-lsp-base/type:lsp-null) + ((process-id :type (common-lisp:or lem-lsp-base/type:lsp-integer lem-lsp-base/type:lsp-null) :initarg :process-id :accessor _initialize-params-process-id :documentation "The process Id of the parent process that started the server. @@ -4606,14 +4733,14 @@ If the parent process is not alive then the server should exit.") (lem-lsp-base/type:lsp-interface ((name :type lem-lsp-base/type:lsp-string :documentation "The name of the client as defined by the client.") - (version :type lem-lsp-base/type:lsp-string :optional common-lisp:t - :documentation "The client's version as defined by the client."))) + (version :type lem-lsp-base/type:lsp-string :optional common-lisp:t :documentation + "The client's version as defined by the client."))) :initarg :client-info :accessor _initialize-params-client-info :optional common-lisp:t :since "3.15.0" :documentation "Information about the client @since 3.15.0") - (locale :type lem-lsp-base/type:lsp-string :initarg :locale :accessor - _initialize-params-locale :optional common-lisp:t :since "3.16.0" :documentation + (locale :type lem-lsp-base/type:lsp-string :initarg :locale :accessor _initialize-params-locale + :optional common-lisp:t :since "3.16.0" :documentation "The locale the client is currently showing the user interface in. This must not necessarily be the locale of the operating system. @@ -4622,17 +4749,13 @@ Uses IETF language tags as the value's syntax (See https://en.wikipedia.org/wiki/IETF_language_tag) @since 3.16.0") - (root-path :type - (common-lisp:or lem-lsp-base/type:lsp-string - lem-lsp-base/type:lsp-null) + (root-path :type (common-lisp:or lem-lsp-base/type:lsp-string lem-lsp-base/type:lsp-null) :initarg :root-path :accessor _initialize-params-root-path :optional common-lisp:t :deprecated "in favour of rootUri." :documentation "The rootPath of the workspace. Is null if no folder is open. @deprecated in favour of rootUri.") - (root-uri :type - (common-lisp:or lem-lsp-base/type:lsp-document-uri - lem-lsp-base/type:lsp-null) + (root-uri :type (common-lisp:or lem-lsp-base/type:lsp-document-uri lem-lsp-base/type:lsp-null) :initarg :root-uri :accessor _initialize-params-root-uri :deprecated "in favour of workspaceFolders." :documentation "The rootUri of the workspace. Is null if no folder is open. If both `rootPath` and `rootUri` are set @@ -4653,8 +4776,7 @@ folder is open. If both `rootPath` and `rootUri` are set (lem-lsp-base/type:define-class workspace-folders-initialize-params common-lisp:nil ((workspace-folders :type - (common-lisp:or (lem-lsp-base/type:lsp-array workspace-folder) - lem-lsp-base/type:lsp-null) + (common-lisp:or (lem-lsp-base/type:lsp-array workspace-folder) lem-lsp-base/type:lsp-null) :initarg :workspace-folders :accessor workspace-folders-initialize-params-workspace-folders :optional common-lisp:t :since "3.6.0" :documentation "The workspace folders configured in the client when the server starts. @@ -4693,8 +4815,7 @@ TextDocumentSyncKind number.") (completion-provider :type completion-options :initarg :completion-provider :accessor server-capabilities-completion-provider :optional common-lisp:t :documentation "The server provides completion support.") - (hover-provider :type - (common-lisp:or lem-lsp-base/type:lsp-boolean hover-options) :initarg + (hover-provider :type (common-lisp:or lem-lsp-base/type:lsp-boolean hover-options) :initarg :hover-provider :accessor server-capabilities-hover-provider :optional common-lisp:t :documentation "The server provides hover support.") (signature-help-provider :type signature-help-options :initarg :signature-help-provider @@ -4705,10 +4826,9 @@ TextDocumentSyncKind number.") declaration-registration-options) :initarg :declaration-provider :accessor server-capabilities-declaration-provider :optional common-lisp:t :documentation "The server provides Goto Declaration support.") - (definition-provider :type - (common-lisp:or lem-lsp-base/type:lsp-boolean definition-options) :initarg - :definition-provider :accessor server-capabilities-definition-provider :optional common-lisp:t - :documentation "The server provides goto definition support.") + (definition-provider :type (common-lisp:or lem-lsp-base/type:lsp-boolean definition-options) + :initarg :definition-provider :accessor server-capabilities-definition-provider :optional + common-lisp:t :documentation "The server provides goto definition support.") (type-definition-provider :type (common-lisp:or lem-lsp-base/type:lsp-boolean type-definition-options type-definition-registration-options) @@ -4719,21 +4839,19 @@ TextDocumentSyncKind number.") implementation-registration-options) :initarg :implementation-provider :accessor server-capabilities-implementation-provider :optional common-lisp:t :documentation "The server provides Goto Implementation support.") - (references-provider :type - (common-lisp:or lem-lsp-base/type:lsp-boolean reference-options) :initarg - :references-provider :accessor server-capabilities-references-provider :optional common-lisp:t - :documentation "The server provides find references support.") + (references-provider :type (common-lisp:or lem-lsp-base/type:lsp-boolean reference-options) + :initarg :references-provider :accessor server-capabilities-references-provider :optional + common-lisp:t :documentation "The server provides find references support.") (document-highlight-provider :type - (common-lisp:or lem-lsp-base/type:lsp-boolean document-highlight-options) - :initarg :document-highlight-provider :accessor server-capabilities-document-highlight-provider + (common-lisp:or lem-lsp-base/type:lsp-boolean document-highlight-options) :initarg + :document-highlight-provider :accessor server-capabilities-document-highlight-provider :optional common-lisp:t :documentation "The server provides document highlight support.") (document-symbol-provider :type - (common-lisp:or lem-lsp-base/type:lsp-boolean document-symbol-options) - :initarg :document-symbol-provider :accessor server-capabilities-document-symbol-provider - :optional common-lisp:t :documentation "The server provides document symbol support.") - (code-action-provider :type - (common-lisp:or lem-lsp-base/type:lsp-boolean code-action-options) :initarg - :code-action-provider :accessor server-capabilities-code-action-provider :optional + (common-lisp:or lem-lsp-base/type:lsp-boolean document-symbol-options) :initarg + :document-symbol-provider :accessor server-capabilities-document-symbol-provider :optional + common-lisp:t :documentation "The server provides document symbol support.") + (code-action-provider :type (common-lisp:or lem-lsp-base/type:lsp-boolean code-action-options) + :initarg :code-action-provider :accessor server-capabilities-code-action-provider :optional common-lisp:t :documentation "The server provides code actions. CodeActionOptions may only be specified if the client states that it supports `codeActionLiteralSupport` in its initial `initialize` request.") @@ -4749,26 +4867,23 @@ specified if the client states that it supports :initarg :color-provider :accessor server-capabilities-color-provider :optional common-lisp:t :documentation "The server provides color provider support.") (workspace-symbol-provider :type - (common-lisp:or lem-lsp-base/type:lsp-boolean workspace-symbol-options) - :initarg :workspace-symbol-provider :accessor server-capabilities-workspace-symbol-provider - :optional common-lisp:t :documentation "The server provides workspace symbol support.") + (common-lisp:or lem-lsp-base/type:lsp-boolean workspace-symbol-options) :initarg + :workspace-symbol-provider :accessor server-capabilities-workspace-symbol-provider :optional + common-lisp:t :documentation "The server provides workspace symbol support.") (document-formatting-provider :type - (common-lisp:or lem-lsp-base/type:lsp-boolean document-formatting-options) - :initarg :document-formatting-provider :accessor - server-capabilities-document-formatting-provider :optional common-lisp:t :documentation - "The server provides document formatting.") + (common-lisp:or lem-lsp-base/type:lsp-boolean document-formatting-options) :initarg + :document-formatting-provider :accessor server-capabilities-document-formatting-provider + :optional common-lisp:t :documentation "The server provides document formatting.") (document-range-formatting-provider :type - (common-lisp:or lem-lsp-base/type:lsp-boolean - document-range-formatting-options) - :initarg :document-range-formatting-provider :accessor + (common-lisp:or lem-lsp-base/type:lsp-boolean document-range-formatting-options) :initarg + :document-range-formatting-provider :accessor server-capabilities-document-range-formatting-provider :optional common-lisp:t :documentation "The server provides document range formatting.") (document-on-type-formatting-provider :type document-on-type-formatting-options :initarg :document-on-type-formatting-provider :accessor server-capabilities-document-on-type-formatting-provider :optional common-lisp:t :documentation "The server provides document formatting on typing.") - (rename-provider :type - (common-lisp:or lem-lsp-base/type:lsp-boolean rename-options) :initarg + (rename-provider :type (common-lisp:or lem-lsp-base/type:lsp-boolean rename-options) :initarg :rename-provider :accessor server-capabilities-rename-provider :optional common-lisp:t :documentation "The server provides rename support. RenameOptions may only be specified if the client states that it supports @@ -4809,8 +4924,7 @@ specified if the client states that it supports @since 3.16.0") (moniker-provider :type - (common-lisp:or lem-lsp-base/type:lsp-boolean moniker-options - moniker-registration-options) + (common-lisp:or lem-lsp-base/type:lsp-boolean moniker-options moniker-registration-options) :initarg :moniker-provider :accessor server-capabilities-moniker-provider :optional common-lisp:t :since "3.16.0" :documentation "The server provides moniker support. @@ -4842,6 +4956,14 @@ specified if the client states that it supports common-lisp:t :since "3.17.0" :documentation "The server has support for pull model diagnostics. @since 3.17.0") + (inline-completion-provider :type + (common-lisp:or lem-lsp-base/type:lsp-boolean inline-completion-options) :initarg + :inline-completion-provider :accessor server-capabilities-inline-completion-provider :optional + common-lisp:t :proposed common-lisp:t :since "3.18.0" :documentation + "Inline completion options used during static registration. + +@since 3.18.0 +@proposed") (workspace :type (lem-lsp-base/type:lsp-interface ((workspace-folders :type workspace-folders-server-capabilities :optional common-lisp:t :since @@ -4868,15 +4990,15 @@ server.")) (lem-lsp-base/type:define-class save-options common-lisp:nil - ((include-text :type lem-lsp-base/type:lsp-boolean :initarg :include-text - :accessor save-options-include-text :optional common-lisp:t :documentation + ((include-text :type lem-lsp-base/type:lsp-boolean :initarg :include-text :accessor + save-options-include-text :optional common-lisp:t :documentation "The client is supposed to include the content on save.")) (:documentation "Save options.")) (lem-lsp-base/type:define-class file-event common-lisp:nil - ((uri :type lem-lsp-base/type:lsp-document-uri :initarg :uri :accessor - file-event-uri :documentation "The file's uri.") + ((uri :type lem-lsp-base/type:lsp-document-uri :initarg :uri :accessor file-event-uri + :documentation "The file's uri.") (type :type file-change-type :initarg :type :accessor file-event-type :documentation "The change type.")) (:documentation "An event describing a file change.")) @@ -4901,10 +5023,8 @@ which is 7."))) common-lisp:t :documentation "The diagnostic's severity. Can be omitted. If omitted it is up to the client to interpret diagnostics as error, warning, info or hint.") - (code :type - (common-lisp:or lem-lsp-base/type:lsp-integer - lem-lsp-base/type:lsp-string) - :initarg :code :accessor diagnostic-code :optional common-lisp:t :documentation + (code :type (common-lisp:or lem-lsp-base/type:lsp-integer lem-lsp-base/type:lsp-string) :initarg + :code :accessor diagnostic-code :optional common-lisp:t :documentation "The diagnostic's code, which usually appear in the user interface.") (code-description :type code-description :initarg :code-description :accessor diagnostic-code-description :optional common-lisp:t :since "3.16.0" :documentation @@ -4912,21 +5032,18 @@ client to interpret diagnostics as error, warning, info or hint.") Requires the code field (above) to be present/not null. @since 3.16.0") - (source :type lem-lsp-base/type:lsp-string :initarg :source :accessor - diagnostic-source :optional common-lisp:t :documentation - "A human-readable string describing the source of this + (source :type lem-lsp-base/type:lsp-string :initarg :source :accessor diagnostic-source + :optional common-lisp:t :documentation "A human-readable string describing the source of this diagnostic, e.g. 'typescript' or 'super lint'. It usually appears in the user interface.") - (message :type lem-lsp-base/type:lsp-string :initarg :message :accessor - diagnostic-message :documentation - "The diagnostic's message. It usually appears in the user interface") - (tags :type (lem-lsp-base/type:lsp-array diagnostic-tag) :initarg :tags - :accessor diagnostic-tags :optional common-lisp:t :since "3.15.0" :documentation + (message :type lem-lsp-base/type:lsp-string :initarg :message :accessor diagnostic-message + :documentation "The diagnostic's message. It usually appears in the user interface") + (tags :type (lem-lsp-base/type:lsp-array diagnostic-tag) :initarg :tags :accessor + diagnostic-tags :optional common-lisp:t :since "3.15.0" :documentation "Additional metadata about the diagnostic. @since 3.15.0") - (related-information :type - (lem-lsp-base/type:lsp-array diagnostic-related-information) :initarg + (related-information :type (lem-lsp-base/type:lsp-array diagnostic-related-information) :initarg :related-information :accessor diagnostic-related-information :optional common-lisp:t :documentation "An array of related diagnostic information, e.g. when symbol-names within a scope collide all definitions can be marked via this property.") @@ -4943,9 +5060,9 @@ are only valid in the scope of a resource.")) common-lisp:nil ((trigger-kind :type completion-trigger-kind :initarg :trigger-kind :accessor completion-context-trigger-kind :documentation "How the completion was triggered.") - (trigger-character :type lem-lsp-base/type:lsp-string :initarg - :trigger-character :accessor completion-context-trigger-character :optional common-lisp:t - :documentation "The trigger character (a single character) that has trigger code complete. + (trigger-character :type lem-lsp-base/type:lsp-string :initarg :trigger-character :accessor + completion-context-trigger-character :optional common-lisp:t :documentation + "The trigger character (a single character) that has trigger code complete. Is undefined if `triggerKind !== CompletionTriggerKind.TriggerCharacter`")) (:documentation "Contains additional information about the context in which a completion request is triggered.")) @@ -4956,8 +5073,8 @@ Is undefined if `triggerKind !== CompletionTriggerKind.TriggerCharacter`")) completion-item-label-details-detail :optional common-lisp:t :documentation "An optional string which is rendered less prominently directly after {@link CompletionItem.label label}, without any spacing. Should be used for function signatures and type annotations.") - (description :type lem-lsp-base/type:lsp-string :initarg :description - :accessor completion-item-label-details-description :optional common-lisp:t :documentation + (description :type lem-lsp-base/type:lsp-string :initarg :description :accessor + completion-item-label-details-description :optional common-lisp:t :documentation "An optional string which is rendered less prominently after {@link CompletionItem.detail}. Should be used for fully qualified names and file paths.")) (:since "3.17.0") @@ -4980,11 +5097,9 @@ for fully qualified names and file paths.")) (lem-lsp-base/type:define-class completion-options (work-done-progress-options) - ((trigger-characters :type - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-string) - :initarg :trigger-characters :accessor completion-options-trigger-characters :optional - common-lisp:t :documentation + ((trigger-characters :type (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-string) :initarg + :trigger-characters :accessor completion-options-trigger-characters :optional common-lisp:t + :documentation "Most tools trigger completion request automatically without explicitly requesting it using a keyboard shortcut (e.g. Ctrl+Space). Typically they do so when the user starts to type an identifier. For example if the user types `c` in a JavaScript file @@ -4993,10 +5108,8 @@ completion item. Characters that make up identifiers don't need to be listed her If code complete should automatically be trigger on characters not being valid inside an identifier (for example `.` in JavaScript) list them in `triggerCharacters`.") - (all-commit-characters :type - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-string) - :initarg :all-commit-characters :accessor completion-options-all-commit-characters :optional + (all-commit-characters :type (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-string) :initarg + :all-commit-characters :accessor completion-options-all-commit-characters :optional common-lisp:t :since "3.2.0" :documentation "The list of all possible characters that commit a completion. This field can be used if clients don't support individual commit characters per completion item. See @@ -5006,15 +5119,14 @@ If a server provides both `allCommitCharacters` and commit characters on an indi completion item the ones on the completion item win. @since 3.2.0") - (resolve-provider :type lem-lsp-base/type:lsp-boolean :initarg - :resolve-provider :accessor completion-options-resolve-provider :optional common-lisp:t - :documentation "The server provides support to resolve additional + (resolve-provider :type lem-lsp-base/type:lsp-boolean :initarg :resolve-provider :accessor + completion-options-resolve-provider :optional common-lisp:t :documentation + "The server provides support to resolve additional information for a completion item.") (completion-item :type (lem-lsp-base/type:lsp-interface - ((label-details-support :type lem-lsp-base/type:lsp-boolean :optional - common-lisp:t :since "3.17.0" :documentation - "The server has support for completion item label + ((label-details-support :type lem-lsp-base/type:lsp-boolean :optional common-lisp:t :since + "3.17.0" :documentation "The server has support for completion item label details (see also `CompletionItemLabelDetails`) when receiving a completion item in a resolve call. @@ -5036,13 +5148,13 @@ capabilities. ((trigger-kind :type signature-help-trigger-kind :initarg :trigger-kind :accessor signature-help-context-trigger-kind :documentation "Action that caused signature help to be triggered.") - (trigger-character :type lem-lsp-base/type:lsp-string :initarg - :trigger-character :accessor signature-help-context-trigger-character :optional common-lisp:t - :documentation "Character that caused signature help to be triggered. + (trigger-character :type lem-lsp-base/type:lsp-string :initarg :trigger-character :accessor + signature-help-context-trigger-character :optional common-lisp:t :documentation + "Character that caused signature help to be triggered. This is undefined when `triggerKind !== SignatureHelpTriggerKind.TriggerCharacter`") - (is-retrigger :type lem-lsp-base/type:lsp-boolean :initarg :is-retrigger - :accessor signature-help-context-is-retrigger :documentation + (is-retrigger :type lem-lsp-base/type:lsp-boolean :initarg :is-retrigger :accessor + signature-help-context-is-retrigger :documentation "`true` if signature help was already showing when it was triggered. Retriggers occurs when the signature help is already active and can be caused by actions such as @@ -5061,20 +5173,19 @@ the user navigating through available signatures.")) (lem-lsp-base/type:define-class signature-information common-lisp:nil - ((label :type lem-lsp-base/type:lsp-string :initarg :label :accessor - signature-information-label :documentation "The label of this signature. Will be shown in + ((label :type lem-lsp-base/type:lsp-string :initarg :label :accessor signature-information-label + :documentation "The label of this signature. Will be shown in the UI.") - (documentation :type - (common-lisp:or lem-lsp-base/type:lsp-string markup-content) :initarg + (documentation :type (common-lisp:or lem-lsp-base/type:lsp-string markup-content) :initarg :documentation :accessor signature-information-documentation :optional common-lisp:t :documentation "The human-readable doc-comment of this signature. Will be shown in the UI but can be omitted.") - (parameters :type (lem-lsp-base/type:lsp-array parameter-information) - :initarg :parameters :accessor signature-information-parameters :optional common-lisp:t - :documentation "The parameters of this signature.") - (active-parameter :type lem-lsp-base/type:lsp-uinteger :initarg - :active-parameter :accessor signature-information-active-parameter :optional common-lisp:t - :since "3.16.0" :documentation "The index of the active parameter. + (parameters :type (lem-lsp-base/type:lsp-array parameter-information) :initarg :parameters + :accessor signature-information-parameters :optional common-lisp:t :documentation + "The parameters of this signature.") + (active-parameter :type lem-lsp-base/type:lsp-uinteger :initarg :active-parameter :accessor + signature-information-active-parameter :optional common-lisp:t :since "3.16.0" :documentation + "The index of the active parameter. If provided, this is used in place of `SignatureHelp.activeParameter`. @@ -5085,15 +5196,11 @@ a set of parameters.")) (lem-lsp-base/type:define-class signature-help-options (work-done-progress-options) - ((trigger-characters :type - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-string) - :initarg :trigger-characters :accessor signature-help-options-trigger-characters :optional - common-lisp:t :documentation "List of characters that trigger signature help automatically.") - (retrigger-characters :type - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-string) - :initarg :retrigger-characters :accessor signature-help-options-retrigger-characters :optional + ((trigger-characters :type (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-string) :initarg + :trigger-characters :accessor signature-help-options-trigger-characters :optional common-lisp:t + :documentation "List of characters that trigger signature help automatically.") + (retrigger-characters :type (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-string) :initarg + :retrigger-characters :accessor signature-help-options-retrigger-characters :optional common-lisp:t :since "3.15.0" :documentation "List of characters that re-trigger signature help. These trigger characters are only active when signature help is already showing. All trigger characters @@ -5109,8 +5216,8 @@ are also counted as re-trigger characters. (lem-lsp-base/type:define-class reference-context common-lisp:nil - ((include-declaration :type lem-lsp-base/type:lsp-boolean :initarg - :include-declaration :accessor reference-context-include-declaration :documentation + ((include-declaration :type lem-lsp-base/type:lsp-boolean :initarg :include-declaration :accessor + reference-context-include-declaration :documentation "Include the declaration of the current symbol.")) (:documentation "Value-object that contains additional information when requesting references.")) @@ -5127,17 +5234,17 @@ requesting references.")) (lem-lsp-base/type:define-class base-symbol-information common-lisp:nil - ((name :type lem-lsp-base/type:lsp-string :initarg :name :accessor - base-symbol-information-name :documentation "The name of this symbol.") + ((name :type lem-lsp-base/type:lsp-string :initarg :name :accessor base-symbol-information-name + :documentation "The name of this symbol.") (kind :type symbol-kind :initarg :kind :accessor base-symbol-information-kind :documentation "The kind of this symbol.") - (tags :type (lem-lsp-base/type:lsp-array symbol-tag) :initarg :tags - :accessor base-symbol-information-tags :optional common-lisp:t :since "3.16.0" :documentation + (tags :type (lem-lsp-base/type:lsp-array symbol-tag) :initarg :tags :accessor + base-symbol-information-tags :optional common-lisp:t :since "3.16.0" :documentation "Tags for this symbol. @since 3.16.0") - (container-name :type lem-lsp-base/type:lsp-string :initarg :container-name - :accessor base-symbol-information-container-name :optional common-lisp:t :documentation + (container-name :type lem-lsp-base/type:lsp-string :initarg :container-name :accessor + base-symbol-information-container-name :optional common-lisp:t :documentation "The name of the symbol containing this symbol. This information is for user interface purposes (e.g. to render a qualifier in the user interface if necessary). It can't be used to re-infer a hierarchy for the document @@ -5156,15 +5263,15 @@ are shown for the same document. (lem-lsp-base/type:define-class code-action-context common-lisp:nil - ((diagnostics :type (lem-lsp-base/type:lsp-array diagnostic) :initarg - :diagnostics :accessor code-action-context-diagnostics :documentation + ((diagnostics :type (lem-lsp-base/type:lsp-array diagnostic) :initarg :diagnostics :accessor + code-action-context-diagnostics :documentation "An array of diagnostics known on the client side overlapping the range provided to the `textDocument/codeAction` request. They are provided so that the server knows which errors are currently presented to the user for the given range. There is no guarantee that these accurately reflect the error state of the resource. The primary parameter to compute code actions is the provided range.") - (only :type (lem-lsp-base/type:lsp-array code-action-kind) :initarg :only - :accessor code-action-context-only :optional common-lisp:t :documentation + (only :type (lem-lsp-base/type:lsp-array code-action-kind) :initarg :only :accessor + code-action-context-only :optional common-lisp:t :documentation "Requested kind of actions to return. Actions not of this kind are filtered out by the client before being shown. So servers @@ -5179,15 +5286,15 @@ a {@link CodeActionProvider.provideCodeActions code action} is run.")) (lem-lsp-base/type:define-class code-action-options (work-done-progress-options) - ((code-action-kinds :type (lem-lsp-base/type:lsp-array code-action-kind) - :initarg :code-action-kinds :accessor code-action-options-code-action-kinds :optional - common-lisp:t :documentation "CodeActionKinds that this server may return. + ((code-action-kinds :type (lem-lsp-base/type:lsp-array code-action-kind) :initarg + :code-action-kinds :accessor code-action-options-code-action-kinds :optional common-lisp:t + :documentation "CodeActionKinds that this server may return. The list of kinds may be generic, such as `CodeActionKind.Refactor`, or the server may list out every specific kind they provide.") - (resolve-provider :type lem-lsp-base/type:lsp-boolean :initarg - :resolve-provider :accessor code-action-options-resolve-provider :optional common-lisp:t :since - "3.16.0" :documentation "The server provides support to resolve additional + (resolve-provider :type lem-lsp-base/type:lsp-boolean :initarg :resolve-provider :accessor + code-action-options-resolve-provider :optional common-lisp:t :since "3.16.0" :documentation + "The server provides support to resolve additional information for a code action. @since 3.16.0")) @@ -5195,9 +5302,9 @@ information for a code action. (lem-lsp-base/type:define-class workspace-symbol-options (work-done-progress-options) - ((resolve-provider :type lem-lsp-base/type:lsp-boolean :initarg - :resolve-provider :accessor workspace-symbol-options-resolve-provider :optional common-lisp:t - :since "3.17.0" :documentation "The server provides support to resolve additional + ((resolve-provider :type lem-lsp-base/type:lsp-boolean :initarg :resolve-provider :accessor + workspace-symbol-options-resolve-provider :optional common-lisp:t :since "3.17.0" + :documentation "The server provides support to resolve additional information for a workspace symbol. @since 3.17.0")) @@ -5205,38 +5312,36 @@ information for a workspace symbol. (lem-lsp-base/type:define-class code-lens-options (work-done-progress-options) - ((resolve-provider :type lem-lsp-base/type:lsp-boolean :initarg - :resolve-provider :accessor code-lens-options-resolve-provider :optional common-lisp:t - :documentation "Code lens has a resolve provider as well.")) + ((resolve-provider :type lem-lsp-base/type:lsp-boolean :initarg :resolve-provider :accessor + code-lens-options-resolve-provider :optional common-lisp:t :documentation + "Code lens has a resolve provider as well.")) (:documentation "Code Lens provider options of a {@link CodeLensRequest}.")) (lem-lsp-base/type:define-class document-link-options (work-done-progress-options) - ((resolve-provider :type lem-lsp-base/type:lsp-boolean :initarg - :resolve-provider :accessor document-link-options-resolve-provider :optional common-lisp:t - :documentation "Document links have a resolve provider as well.")) + ((resolve-provider :type lem-lsp-base/type:lsp-boolean :initarg :resolve-provider :accessor + document-link-options-resolve-provider :optional common-lisp:t :documentation + "Document links have a resolve provider as well.")) (:documentation "Provider options for a {@link DocumentLinkRequest}.")) (lem-lsp-base/type:define-class formatting-options common-lisp:nil ((tab-size :type lem-lsp-base/type:lsp-uinteger :initarg :tab-size :accessor formatting-options-tab-size :documentation "Size of a tab in spaces.") - (insert-spaces :type lem-lsp-base/type:lsp-boolean :initarg :insert-spaces - :accessor formatting-options-insert-spaces :documentation "Prefer spaces over tabs.") - (trim-trailing-whitespace :type lem-lsp-base/type:lsp-boolean :initarg - :trim-trailing-whitespace :accessor formatting-options-trim-trailing-whitespace :optional - common-lisp:t :since "3.15.0" :documentation "Trim trailing whitespace on a line. + (insert-spaces :type lem-lsp-base/type:lsp-boolean :initarg :insert-spaces :accessor + formatting-options-insert-spaces :documentation "Prefer spaces over tabs.") + (trim-trailing-whitespace :type lem-lsp-base/type:lsp-boolean :initarg :trim-trailing-whitespace + :accessor formatting-options-trim-trailing-whitespace :optional common-lisp:t :since "3.15.0" + :documentation "Trim trailing whitespace on a line. @since 3.15.0") - (insert-final-newline :type lem-lsp-base/type:lsp-boolean :initarg - :insert-final-newline :accessor formatting-options-insert-final-newline :optional common-lisp:t - :since "3.15.0" :documentation - "Insert a newline character at the end of the file if one does not exist. + (insert-final-newline :type lem-lsp-base/type:lsp-boolean :initarg :insert-final-newline + :accessor formatting-options-insert-final-newline :optional common-lisp:t :since "3.15.0" + :documentation "Insert a newline character at the end of the file if one does not exist. @since 3.15.0") - (trim-final-newlines :type lem-lsp-base/type:lsp-boolean :initarg - :trim-final-newlines :accessor formatting-options-trim-final-newlines :optional common-lisp:t - :since "3.15.0" :documentation + (trim-final-newlines :type lem-lsp-base/type:lsp-boolean :initarg :trim-final-newlines :accessor + formatting-options-trim-final-newlines :optional common-lisp:t :since "3.15.0" :documentation "Trim all newlines after the final newline at the end of the file. @since 3.15.0")) @@ -5249,17 +5354,21 @@ information for a workspace symbol. (lem-lsp-base/type:define-class document-range-formatting-options (work-done-progress-options) - common-lisp:nil + ((ranges-support :type lem-lsp-base/type:lsp-boolean :initarg :ranges-support :accessor + document-range-formatting-options-ranges-support :optional common-lisp:t :proposed + common-lisp:t :since "3.18.0" :documentation + "Whether the server supports formatting multiple ranges at once. + +@since 3.18.0 +@proposed")) (:documentation "Provider options for a {@link DocumentRangeFormattingRequest}.")) (lem-lsp-base/type:define-class document-on-type-formatting-options common-lisp:nil - ((first-trigger-character :type lem-lsp-base/type:lsp-string :initarg - :first-trigger-character :accessor document-on-type-formatting-options-first-trigger-character - :documentation "A character on which formatting should be triggered, like `{`.") - (more-trigger-character :type - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-string) + ((first-trigger-character :type lem-lsp-base/type:lsp-string :initarg :first-trigger-character + :accessor document-on-type-formatting-options-first-trigger-character :documentation + "A character on which formatting should be triggered, like `{`.") + (more-trigger-character :type (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-string) :initarg :more-trigger-character :accessor document-on-type-formatting-options-more-trigger-character :optional common-lisp:t :documentation "More trigger characters.")) @@ -5267,42 +5376,34 @@ information for a workspace symbol. (lem-lsp-base/type:define-class rename-options (work-done-progress-options) - ((prepare-provider :type lem-lsp-base/type:lsp-boolean :initarg - :prepare-provider :accessor rename-options-prepare-provider :optional common-lisp:t :since - "version 3.12.0" :documentation "Renames should be checked and tested before being executed. + ((prepare-provider :type lem-lsp-base/type:lsp-boolean :initarg :prepare-provider :accessor + rename-options-prepare-provider :optional common-lisp:t :since "version 3.12.0" :documentation + "Renames should be checked and tested before being executed. @since version 3.12.0")) (:documentation "Provider options for a {@link RenameRequest}.")) (lem-lsp-base/type:define-class execute-command-options (work-done-progress-options) - ((commands :type - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-string) - :initarg :commands :accessor execute-command-options-commands :documentation + ((commands :type (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-string) :initarg :commands + :accessor execute-command-options-commands :documentation "The commands to be executed on the server")) (:documentation "The server capabilities of a {@link ExecuteCommandRequest}.")) (lem-lsp-base/type:define-class semantic-tokens-legend common-lisp:nil - ((token-types :type - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-string) - :initarg :token-types :accessor semantic-tokens-legend-token-types :documentation + ((token-types :type (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-string) :initarg + :token-types :accessor semantic-tokens-legend-token-types :documentation "The token types a server uses.") - (token-modifiers :type - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-string) - :initarg :token-modifiers :accessor semantic-tokens-legend-token-modifiers :documentation + (token-modifiers :type (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-string) :initarg + :token-modifiers :accessor semantic-tokens-legend-token-modifiers :documentation "The token modifiers a server uses.")) (:since "3.16.0") (:documentation "@since 3.16.0")) (lem-lsp-base/type:define-class optional-versioned-text-document-identifier (text-document-identifier) - ((version :type - (common-lisp:or lem-lsp-base/type:lsp-integer - lem-lsp-base/type:lsp-null) + ((version :type (common-lisp:or lem-lsp-base/type:lsp-integer lem-lsp-base/type:lsp-null) :initarg :version :accessor optional-versioned-text-document-identifier-version :documentation "The version number of this document. If a versioned text document identifier is sent from the server to the client and the file is not open in the editor @@ -5324,8 +5425,8 @@ truth (as specified with document content ownership).")) (lem-lsp-base/type:define-class resource-operation common-lisp:nil - ((kind :type lem-lsp-base/type:lsp-string :initarg :kind :accessor - resource-operation-kind :documentation "The resource operation kind.") + ((kind :type lem-lsp-base/type:lsp-string :initarg :kind :accessor resource-operation-kind + :documentation "The resource operation kind.") (annotation-id :type change-annotation-identifier :initarg :annotation-id :accessor resource-operation-annotation-id :optional common-lisp:t :since "3.16.0" :documentation "An optional annotation identifier describing the operation. @@ -5338,9 +5439,9 @@ truth (as specified with document content ownership).")) ((overwrite :type lem-lsp-base/type:lsp-boolean :initarg :overwrite :accessor create-file-options-overwrite :optional common-lisp:t :documentation "Overwrite existing file. Overwrite wins over `ignoreIfExists`") - (ignore-if-exists :type lem-lsp-base/type:lsp-boolean :initarg - :ignore-if-exists :accessor create-file-options-ignore-if-exists :optional common-lisp:t - :documentation "Ignore if exists.")) + (ignore-if-exists :type lem-lsp-base/type:lsp-boolean :initarg :ignore-if-exists :accessor + create-file-options-ignore-if-exists :optional common-lisp:t :documentation + "Ignore if exists.")) (:documentation "Options to create a file.")) (lem-lsp-base/type:define-class rename-file-options @@ -5348,9 +5449,9 @@ truth (as specified with document content ownership).")) ((overwrite :type lem-lsp-base/type:lsp-boolean :initarg :overwrite :accessor rename-file-options-overwrite :optional common-lisp:t :documentation "Overwrite target if existing. Overwrite wins over `ignoreIfExists`") - (ignore-if-exists :type lem-lsp-base/type:lsp-boolean :initarg - :ignore-if-exists :accessor rename-file-options-ignore-if-exists :optional common-lisp:t - :documentation "Ignores if target exists.")) + (ignore-if-exists :type lem-lsp-base/type:lsp-boolean :initarg :ignore-if-exists :accessor + rename-file-options-ignore-if-exists :optional common-lisp:t :documentation + "Ignores if target exists.")) (:documentation "Rename file options")) (lem-lsp-base/type:define-class delete-file-options @@ -5358,16 +5459,15 @@ truth (as specified with document content ownership).")) ((recursive :type lem-lsp-base/type:lsp-boolean :initarg :recursive :accessor delete-file-options-recursive :optional common-lisp:t :documentation "Delete the content recursively if a folder is denoted.") - (ignore-if-not-exists :type lem-lsp-base/type:lsp-boolean :initarg - :ignore-if-not-exists :accessor delete-file-options-ignore-if-not-exists :optional - common-lisp:t :documentation "Ignore the operation if the file doesn't exist.")) + (ignore-if-not-exists :type lem-lsp-base/type:lsp-boolean :initarg :ignore-if-not-exists + :accessor delete-file-options-ignore-if-not-exists :optional common-lisp:t :documentation + "Ignore the operation if the file doesn't exist.")) (:documentation "Delete file options")) (lem-lsp-base/type:define-class file-operation-pattern common-lisp:nil - ((glob :type lem-lsp-base/type:lsp-string :initarg :glob :accessor - file-operation-pattern-glob :documentation - "The glob pattern to match. Glob patterns can have the following syntax: + ((glob :type lem-lsp-base/type:lsp-string :initarg :glob :accessor file-operation-pattern-glob + :documentation "The glob pattern to match. Glob patterns can have the following syntax: - `*` to match one or more characters in a path segment - `?` to match on one character in a path segment - `**` to match any number of path segments, including none @@ -5393,9 +5493,7 @@ the server is interested in receiving. ((uri :type lem-lsp-base/type:lsp-document-uri :initarg :uri :accessor workspace-full-document-diagnostic-report-uri :documentation "The URI for which diagnostic information is reported.") - (version :type - (common-lisp:or lem-lsp-base/type:lsp-integer - lem-lsp-base/type:lsp-null) + (version :type (common-lisp:or lem-lsp-base/type:lsp-integer lem-lsp-base/type:lsp-null) :initarg :version :accessor workspace-full-document-diagnostic-report-version :documentation "The version number for which the diagnostics are reported. If the document is not marked as open `null` can be provided.")) @@ -5409,9 +5507,7 @@ If the document is not marked as open `null` can be provided.")) ((uri :type lem-lsp-base/type:lsp-document-uri :initarg :uri :accessor workspace-unchanged-document-diagnostic-report-uri :documentation "The URI for which diagnostic information is reported.") - (version :type - (common-lisp:or lem-lsp-base/type:lsp-integer - lem-lsp-base/type:lsp-null) + (version :type (common-lisp:or lem-lsp-base/type:lsp-integer lem-lsp-base/type:lsp-null) :initarg :version :accessor workspace-unchanged-document-diagnostic-report-version :documentation "The version number for which the diagnostics are reported. If the document is not marked as open `null` can be provided.")) @@ -5424,8 +5520,8 @@ If the document is not marked as open `null` can be provided.")) common-lisp:nil ((kind :type notebook-cell-kind :initarg :kind :accessor notebook-cell-kind :documentation "The cell's kind") - (document :type lem-lsp-base/type:lsp-document-uri :initarg :document - :accessor notebook-cell-document :documentation "The URI of the cell's text document + (document :type lem-lsp-base/type:lsp-document-uri :initarg :document :accessor + notebook-cell-document :documentation "The URI of the cell's text document content.") (metadata :type lsp-object :initarg :metadata :accessor notebook-cell-metadata :optional common-lisp:t :documentation "Additional metadata stored with the cell. @@ -5448,10 +5544,10 @@ notebook cell or the cell's text document. common-lisp:nil ((start :type lem-lsp-base/type:lsp-uinteger :initarg :start :accessor notebook-cell-array-change-start :documentation "The start oftest of the cell that changed.") - (delete-count :type lem-lsp-base/type:lsp-uinteger :initarg :delete-count - :accessor notebook-cell-array-change-delete-count :documentation "The deleted cells") - (cells :type (lem-lsp-base/type:lsp-array notebook-cell) :initarg :cells - :accessor notebook-cell-array-change-cells :optional common-lisp:t :documentation + (delete-count :type lem-lsp-base/type:lsp-uinteger :initarg :delete-count :accessor + notebook-cell-array-change-delete-count :documentation "The deleted cells") + (cells :type (lem-lsp-base/type:lsp-array notebook-cell) :initarg :cells :accessor + notebook-cell-array-change-cells :optional common-lisp:t :documentation "The new cells, if any")) (:since "3.17.0") (:documentation "A change describing how to move a `NotebookCell` @@ -5459,6 +5555,19 @@ array from state S to S'. @since 3.17.0")) +(lem-lsp-base/type:define-class selected-completion-info + common-lisp:nil + ((range :type range :initarg :range :accessor selected-completion-info-range :documentation + "The range that will be replaced if this completion item is accepted.") + (text :type lem-lsp-base/type:lsp-string :initarg :text :accessor selected-completion-info-text + :documentation "The text the range will be replaced with if this completion is accepted.")) + (:proposed common-lisp:t) + (:since "3.18.0") + (:documentation "Describes the currently selected completion item. + +@since 3.18.0 +@proposed")) + (lem-lsp-base/type:define-class client-capabilities common-lisp:nil ((workspace :type workspace-client-capabilities :initarg :workspace :accessor @@ -5485,8 +5594,8 @@ array from state S to S'. (lem-lsp-base/type:define-class text-document-sync-options common-lisp:nil - ((open-close :type lem-lsp-base/type:lsp-boolean :initarg :open-close - :accessor text-document-sync-options-open-close :optional common-lisp:t :documentation + ((open-close :type lem-lsp-base/type:lsp-boolean :initarg :open-close :accessor + text-document-sync-options-open-close :optional common-lisp:t :documentation "Open and close notifications are sent to the server. If omitted open close notification should not be sent.") (change :type text-document-sync-kind :initarg :change :accessor @@ -5497,13 +5606,13 @@ and TextDocumentSyncKind.Incremental. If omitted it defaults to TextDocumentSync text-document-sync-options-will-save :optional common-lisp:t :documentation "If present will save notifications are sent to the server. If omitted the notification should not be sent.") - (will-save-wait-until :type lem-lsp-base/type:lsp-boolean :initarg - :will-save-wait-until :accessor text-document-sync-options-will-save-wait-until :optional - common-lisp:t :documentation + (will-save-wait-until :type lem-lsp-base/type:lsp-boolean :initarg :will-save-wait-until + :accessor text-document-sync-options-will-save-wait-until :optional common-lisp:t + :documentation "If present will save wait until requests are sent to the server. If omitted the request should not be sent.") - (save :type (common-lisp:or lem-lsp-base/type:lsp-boolean save-options) - :initarg :save :accessor text-document-sync-options-save :optional common-lisp:t :documentation + (save :type (common-lisp:or lem-lsp-base/type:lsp-boolean save-options) :initarg :save :accessor + text-document-sync-options-save :optional common-lisp:t :documentation "If present save notifications are sent to the server. If omitted the notification should not be sent."))) @@ -5513,27 +5622,23 @@ sent."))) (lem-lsp-base/type:lsp-array (common-lisp:or (lem-lsp-base/type:lsp-interface - ((notebook :type - (common-lisp:or lem-lsp-base/type:lsp-string notebook-document-filter) + ((notebook :type (common-lisp:or lem-lsp-base/type:lsp-string notebook-document-filter) :documentation "The notebook to be synced If a string value is provided it matches against the notebook type. '*' matches every notebook.") (cells :type (lem-lsp-base/type:lsp-array - (lem-lsp-base/type:lsp-interface - ((language :type lem-lsp-base/type:lsp-string)))) + (lem-lsp-base/type:lsp-interface ((language :type lem-lsp-base/type:lsp-string)))) :optional common-lisp:t :documentation "The cells of the matching notebook to be synced."))) (lem-lsp-base/type:lsp-interface - ((notebook :type - (common-lisp:or lem-lsp-base/type:lsp-string notebook-document-filter) + ((notebook :type (common-lisp:or lem-lsp-base/type:lsp-string notebook-document-filter) :optional common-lisp:t :documentation "The notebook to be synced If a string value is provided it matches against the notebook type. '*' matches every notebook.") (cells :type (lem-lsp-base/type:lsp-array - (lem-lsp-base/type:lsp-interface - ((language :type lem-lsp-base/type:lsp-string)))) + (lem-lsp-base/type:lsp-interface ((language :type lem-lsp-base/type:lsp-string)))) :documentation "The cells of the matching notebook to be synced."))))) :initarg :notebook-selector :accessor notebook-document-sync-options-notebook-selector :documentation "The notebooks to be synced") @@ -5570,11 +5675,9 @@ cell will be synced. workspace-folders-server-capabilities-supported :optional common-lisp:t :documentation "The server has support for workspace folders") (change-notifications :type - (common-lisp:or lem-lsp-base/type:lsp-string - lem-lsp-base/type:lsp-boolean) - :initarg :change-notifications :accessor - workspace-folders-server-capabilities-change-notifications :optional common-lisp:t - :documentation "Whether the server wants to receive workspace folder + (common-lisp:or lem-lsp-base/type:lsp-string lem-lsp-base/type:lsp-boolean) :initarg + :change-notifications :accessor workspace-folders-server-capabilities-change-notifications + :optional common-lisp:t :documentation "Whether the server wants to receive workspace folder change notifications. If a string is provided the string is treated as an ID @@ -5609,9 +5712,8 @@ using the `client/unregisterCapability` request."))) (lem-lsp-base/type:define-class code-description common-lisp:nil - ((href :type lem-lsp-base/type:lsp-uri :initarg :href :accessor - code-description-href :documentation - "An URI to open with more information about the diagnostic error.")) + ((href :type lem-lsp-base/type:lsp-uri :initarg :href :accessor code-description-href + :documentation "An URI to open with more information about the diagnostic error.")) (:since "3.16.0") (:documentation "Structure to capture a description for an error code. @@ -5633,8 +5735,7 @@ a symbol in a scope.")) common-lisp:nil ((label :type (common-lisp:or lem-lsp-base/type:lsp-string - (lem-lsp-base/type:lsp-tuple - lem-lsp-base/type:lsp-uinteger + (lem-lsp-base/type:lsp-tuple lem-lsp-base/type:lsp-uinteger lem-lsp-base/type:lsp-uinteger)) :initarg :label :accessor parameter-information-label :documentation "The label of this parameter information. @@ -5645,8 +5746,7 @@ string representation as `Position` and `Range` does. *Note*: a label of type string should be a substring of its containing signature label. Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`.") - (documentation :type - (common-lisp:or lem-lsp-base/type:lsp-string markup-content) :initarg + (documentation :type (common-lisp:or lem-lsp-base/type:lsp-string markup-content) :initarg :documentation :accessor parameter-information-documentation :optional common-lisp:t :documentation "The human-readable doc-comment of this parameter. Will be shown in the UI but can be omitted.")) @@ -5655,9 +5755,8 @@ have a label and a doc-comment.")) (lem-lsp-base/type:define-class notebook-cell-text-document-filter common-lisp:nil - ((notebook :type - (common-lisp:or lem-lsp-base/type:lsp-string notebook-document-filter) - :initarg :notebook :accessor notebook-cell-text-document-filter-notebook :documentation + ((notebook :type (common-lisp:or lem-lsp-base/type:lsp-string notebook-document-filter) :initarg + :notebook :accessor notebook-cell-text-document-filter-notebook :documentation "A filter that matches against the notebook containing the notebook cell. If a string value is provided it matches against the @@ -5676,8 +5775,8 @@ document by different properties. (lem-lsp-base/type:define-class file-operation-pattern-options common-lisp:nil - ((ignore-case :type lem-lsp-base/type:lsp-boolean :initarg :ignore-case - :accessor file-operation-pattern-options-ignore-case :optional common-lisp:t :documentation + ((ignore-case :type lem-lsp-base/type:lsp-boolean :initarg :ignore-case :accessor + file-operation-pattern-options-ignore-case :optional common-lisp:t :documentation "The pattern should be matched ignoring casing.")) (:since "3.16.0") (:documentation "Matching options for the file operation pattern. @@ -5686,9 +5785,8 @@ document by different properties. (lem-lsp-base/type:define-class execution-summary common-lisp:nil - ((execution-order :type lem-lsp-base/type:lsp-uinteger :initarg - :execution-order :accessor execution-summary-execution-order :documentation - "A strict monotonically increasing value + ((execution-order :type lem-lsp-base/type:lsp-uinteger :initarg :execution-order :accessor + execution-summary-execution-order :documentation "A strict monotonically increasing value indicating the execution order of a cell inside a notebook.") (success :type lem-lsp-base/type:lsp-boolean :initarg :success :accessor @@ -5698,8 +5796,8 @@ not if known by the client."))) (lem-lsp-base/type:define-class workspace-client-capabilities common-lisp:nil - ((apply-edit :type lem-lsp-base/type:lsp-boolean :initarg :apply-edit - :accessor workspace-client-capabilities-apply-edit :optional common-lisp:t :documentation + ((apply-edit :type lem-lsp-base/type:lsp-boolean :initarg :apply-edit :accessor + workspace-client-capabilities-apply-edit :optional common-lisp:t :documentation "The client supports applying batch edits to the workspace by supporting the request 'workspace/applyEdit'") @@ -5720,13 +5818,13 @@ to the workspace by supporting the request (execute-command :type execute-command-client-capabilities :initarg :execute-command :accessor workspace-client-capabilities-execute-command :optional common-lisp:t :documentation "Capabilities specific to the `workspace/executeCommand` request.") - (workspace-folders :type lem-lsp-base/type:lsp-boolean :initarg - :workspace-folders :accessor workspace-client-capabilities-workspace-folders :optional - common-lisp:t :since "3.6.0" :documentation "The client has support for workspace folders. + (workspace-folders :type lem-lsp-base/type:lsp-boolean :initarg :workspace-folders :accessor + workspace-client-capabilities-workspace-folders :optional common-lisp:t :since "3.6.0" + :documentation "The client has support for workspace folders. @since 3.6.0") - (configuration :type lem-lsp-base/type:lsp-boolean :initarg :configuration - :accessor workspace-client-capabilities-configuration :optional common-lisp:t :since "3.6.0" + (configuration :type lem-lsp-base/type:lsp-boolean :initarg :configuration :accessor + workspace-client-capabilities-configuration :optional common-lisp:t :since "3.6.0" :documentation "The client supports `workspace/configuration` requests. @since 3.6.0") @@ -5764,7 +5862,14 @@ workspace. :documentation "Capabilities specific to the diagnostic requests scoped to the workspace. -@since 3.17.0.")) +@since 3.17.0.") + (folding-range :type folding-range-workspace-client-capabilities :initarg :folding-range + :accessor workspace-client-capabilities-folding-range :optional common-lisp:t :proposed + common-lisp:t :since "3.18.0" :documentation + "Capabilities specific to the folding range requests scoped to the workspace. + +@since 3.18.0 +@proposed")) (:documentation "Workspace specific client capabilities.")) (lem-lsp-base/type:define-class text-document-client-capabilities @@ -5889,7 +5994,14 @@ workspace. text-document-client-capabilities-diagnostic :optional common-lisp:t :since "3.17.0" :documentation "Capabilities specific to the diagnostic pull model. -@since 3.17.0")) +@since 3.17.0") + (inline-completion :type inline-completion-client-capabilities :initarg :inline-completion + :accessor text-document-client-capabilities-inline-completion :optional common-lisp:t :proposed + common-lisp:t :since "3.18.0" :documentation + "Client capabilities specific to inline completions. + +@since 3.18.0 +@proposed")) (:documentation "Text document specific client capabilities.")) (lem-lsp-base/type:define-class notebook-document-client-capabilities @@ -5906,10 +6018,9 @@ workspace. (lem-lsp-base/type:define-class window-client-capabilities common-lisp:nil - ((work-done-progress :type lem-lsp-base/type:lsp-boolean :initarg - :work-done-progress :accessor window-client-capabilities-work-done-progress :optional - common-lisp:t :since "3.15.0" :documentation - "It indicates whether the client supports server initiated + ((work-done-progress :type lem-lsp-base/type:lsp-boolean :initarg :work-done-progress :accessor + window-client-capabilities-work-done-progress :optional common-lisp:t :since "3.15.0" + :documentation "It indicates whether the client supports server initiated progress using the `window/workDoneProgress/create` request. The capability also controls Whether client supports handling @@ -5935,9 +6046,7 @@ capabilities. (lem-lsp-base/type:lsp-interface ((cancel :type lem-lsp-base/type:lsp-boolean :documentation "The client will actively cancel the request.") - (retry-on-content-modified :type - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-string) + (retry-on-content-modified :type (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-string) :documentation "The list of requests for which the client will retry the request if it receives a response with error code `ContentModified`"))) @@ -5959,8 +6068,7 @@ anymore since the information is outdated). "Client capabilities specific to the client's markdown parser. @since 3.16.0") - (position-encodings :type - (lem-lsp-base/type:lsp-array position-encoding-kind) :initarg + (position-encodings :type (lem-lsp-base/type:lsp-array position-encoding-kind) :initarg :position-encodings :accessor general-client-capabilities-position-encodings :optional common-lisp:t :since "3.17.0" :documentation "The position encodings supported by the client. Client and server @@ -5988,8 +6096,8 @@ side. (lem-lsp-base/type:define-class relative-pattern common-lisp:nil - ((base-uri :type (common-lisp:or workspace-folder lem-lsp-base/type:lsp-uri) - :initarg :base-uri :accessor relative-pattern-base-uri :documentation + ((base-uri :type (common-lisp:or workspace-folder lem-lsp-base/type:lsp-uri) :initarg :base-uri + :accessor relative-pattern-base-uri :documentation "A workspace folder or a base URI to which this pattern will be matched against relatively.") (pattern :type pattern :initarg :pattern :accessor relative-pattern-pattern :documentation @@ -6003,12 +6111,10 @@ folder root, but it can be another absolute URI as well. (lem-lsp-base/type:define-class workspace-edit-client-capabilities common-lisp:nil - ((document-changes :type lem-lsp-base/type:lsp-boolean :initarg - :document-changes :accessor workspace-edit-client-capabilities-document-changes :optional - common-lisp:t :documentation + ((document-changes :type lem-lsp-base/type:lsp-boolean :initarg :document-changes :accessor + workspace-edit-client-capabilities-document-changes :optional common-lisp:t :documentation "The client supports versioned document changes in `WorkspaceEdit`s") - (resource-operations :type - (lem-lsp-base/type:lsp-array resource-operation-kind) :initarg + (resource-operations :type (lem-lsp-base/type:lsp-array resource-operation-kind) :initarg :resource-operations :accessor workspace-edit-client-capabilities-resource-operations :optional common-lisp:t :since "3.13.0" :documentation "The resource operations the client supports. Clients should at least @@ -6021,9 +6127,9 @@ support 'create', 'rename' and 'delete' files and folders. fails. @since 3.13.0") - (normalizes-line-endings :type lem-lsp-base/type:lsp-boolean :initarg - :normalizes-line-endings :accessor workspace-edit-client-capabilities-normalizes-line-endings - :optional common-lisp:t :since "3.16.0" :documentation + (normalizes-line-endings :type lem-lsp-base/type:lsp-boolean :initarg :normalizes-line-endings + :accessor workspace-edit-client-capabilities-normalizes-line-endings :optional common-lisp:t + :since "3.16.0" :documentation "Whether the client normalizes line endings to the client specific setting. If set to `true` the client will normalize line ending characters @@ -6033,8 +6139,7 @@ character. @since 3.16.0") (change-annotation-support :type (lem-lsp-base/type:lsp-interface - ((groups-on-label :type lem-lsp-base/type:lsp-boolean :optional - common-lisp:t :documentation + ((groups-on-label :type lem-lsp-base/type:lsp-boolean :optional common-lisp:t :documentation "Whether the client groups edits with equal labels into tree nodes, for instance all edits labelled with \"Changes in Strings\" would be a tree node."))) @@ -6048,23 +6153,22 @@ create file, rename file and delete file changes. (lem-lsp-base/type:define-class did-change-configuration-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor - did-change-configuration-client-capabilities-dynamic-registration :optional common-lisp:t - :documentation "Did change configuration notification supports dynamic registration."))) + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor did-change-configuration-client-capabilities-dynamic-registration :optional + common-lisp:t :documentation + "Did change configuration notification supports dynamic registration."))) (lem-lsp-base/type:define-class did-change-watched-files-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor - did-change-watched-files-client-capabilities-dynamic-registration :optional common-lisp:t - :documentation "Did change watched files notification supports dynamic registration. Please note + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor did-change-watched-files-client-capabilities-dynamic-registration :optional + common-lisp:t :documentation + "Did change watched files notification supports dynamic registration. Please note that the current protocol doesn't support static configuration for file changes from the server side.") - (relative-pattern-support :type lem-lsp-base/type:lsp-boolean :initarg - :relative-pattern-support :accessor - did-change-watched-files-client-capabilities-relative-pattern-support :optional common-lisp:t - :since "3.17.0" :documentation + (relative-pattern-support :type lem-lsp-base/type:lsp-boolean :initarg :relative-pattern-support + :accessor did-change-watched-files-client-capabilities-relative-pattern-support :optional + common-lisp:t :since "3.17.0" :documentation "Whether the client has support for {@link RelativePattern relative pattern} or not. @@ -6072,13 +6176,13 @@ or not. (lem-lsp-base/type:define-class workspace-symbol-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor workspace-symbol-client-capabilities-dynamic-registration - :optional common-lisp:t :documentation "Symbol request supports dynamic registration.") + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor workspace-symbol-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Symbol request supports dynamic registration.") (symbol-kind :type (lem-lsp-base/type:lsp-interface - ((value-set :type (lem-lsp-base/type:lsp-array symbol-kind) :optional - common-lisp:t :documentation "The symbol kind values the client supports. When this + ((value-set :type (lem-lsp-base/type:lsp-array symbol-kind) :optional common-lisp:t + :documentation "The symbol kind values the client supports. When this property exists the client also guarantees that it will handle values outside its set gracefully and falls back to a default value when unknown. @@ -6100,10 +6204,8 @@ Clients supporting tags have to handle unknown tags gracefully. @since 3.16.0") (resolve-support :type (lem-lsp-base/type:lsp-interface - ((properties :type - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-string) - :documentation "The properties that a client can resolve lazily. Usually + ((properties :type (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-string) :documentation + "The properties that a client can resolve lazily. Usually `location.range`"))) :initarg :resolve-support :accessor workspace-symbol-client-capabilities-resolve-support :optional common-lisp:t :since "3.17.0" :documentation @@ -6116,17 +6218,16 @@ properties. (lem-lsp-base/type:define-class execute-command-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor execute-command-client-capabilities-dynamic-registration - :optional common-lisp:t :documentation "Execute command supports dynamic registration.")) + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor execute-command-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Execute command supports dynamic registration.")) (:documentation "The client capabilities of a {@link ExecuteCommandRequest}.")) (lem-lsp-base/type:define-class semantic-tokens-workspace-client-capabilities common-lisp:nil - ((refresh-support :type lem-lsp-base/type:lsp-boolean :initarg - :refresh-support :accessor semantic-tokens-workspace-client-capabilities-refresh-support - :optional common-lisp:t :documentation - "Whether the client implementation supports a refresh request sent from + ((refresh-support :type lem-lsp-base/type:lsp-boolean :initarg :refresh-support :accessor + semantic-tokens-workspace-client-capabilities-refresh-support :optional common-lisp:t + :documentation "Whether the client implementation supports a refresh request sent from the server to the client. Note that this event is global and will force the client to refresh all @@ -6138,9 +6239,8 @@ wide change that requires such a calculation.")) (lem-lsp-base/type:define-class code-lens-workspace-client-capabilities common-lisp:nil - ((refresh-support :type lem-lsp-base/type:lsp-boolean :initarg - :refresh-support :accessor code-lens-workspace-client-capabilities-refresh-support :optional - common-lisp:t :documentation + ((refresh-support :type lem-lsp-base/type:lsp-boolean :initarg :refresh-support :accessor + code-lens-workspace-client-capabilities-refresh-support :optional common-lisp:t :documentation "Whether the client implementation supports a refresh request sent from the server to the client. @@ -6153,27 +6253,27 @@ change that requires such a calculation.")) (lem-lsp-base/type:define-class file-operation-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor file-operation-client-capabilities-dynamic-registration - :optional common-lisp:t :documentation + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor file-operation-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether the client supports dynamic registration for file requests/notifications.") - (did-create :type lem-lsp-base/type:lsp-boolean :initarg :did-create - :accessor file-operation-client-capabilities-did-create :optional common-lisp:t :documentation + (did-create :type lem-lsp-base/type:lsp-boolean :initarg :did-create :accessor + file-operation-client-capabilities-did-create :optional common-lisp:t :documentation "The client has support for sending didCreateFiles notifications.") - (will-create :type lem-lsp-base/type:lsp-boolean :initarg :will-create - :accessor file-operation-client-capabilities-will-create :optional common-lisp:t :documentation + (will-create :type lem-lsp-base/type:lsp-boolean :initarg :will-create :accessor + file-operation-client-capabilities-will-create :optional common-lisp:t :documentation "The client has support for sending willCreateFiles requests.") - (did-rename :type lem-lsp-base/type:lsp-boolean :initarg :did-rename - :accessor file-operation-client-capabilities-did-rename :optional common-lisp:t :documentation + (did-rename :type lem-lsp-base/type:lsp-boolean :initarg :did-rename :accessor + file-operation-client-capabilities-did-rename :optional common-lisp:t :documentation "The client has support for sending didRenameFiles notifications.") - (will-rename :type lem-lsp-base/type:lsp-boolean :initarg :will-rename - :accessor file-operation-client-capabilities-will-rename :optional common-lisp:t :documentation + (will-rename :type lem-lsp-base/type:lsp-boolean :initarg :will-rename :accessor + file-operation-client-capabilities-will-rename :optional common-lisp:t :documentation "The client has support for sending willRenameFiles requests.") - (did-delete :type lem-lsp-base/type:lsp-boolean :initarg :did-delete - :accessor file-operation-client-capabilities-did-delete :optional common-lisp:t :documentation + (did-delete :type lem-lsp-base/type:lsp-boolean :initarg :did-delete :accessor + file-operation-client-capabilities-did-delete :optional common-lisp:t :documentation "The client has support for sending didDeleteFiles notifications.") - (will-delete :type lem-lsp-base/type:lsp-boolean :initarg :will-delete - :accessor file-operation-client-capabilities-will-delete :optional common-lisp:t :documentation + (will-delete :type lem-lsp-base/type:lsp-boolean :initarg :will-delete :accessor + file-operation-client-capabilities-will-delete :optional common-lisp:t :documentation "The client has support for sending willDeleteFiles requests.")) (:since "3.16.0") (:documentation "Capabilities relating to events from file operations by the user in the client. @@ -6185,10 +6285,9 @@ like renaming a file in the UI. (lem-lsp-base/type:define-class inline-value-workspace-client-capabilities common-lisp:nil - ((refresh-support :type lem-lsp-base/type:lsp-boolean :initarg - :refresh-support :accessor inline-value-workspace-client-capabilities-refresh-support :optional - common-lisp:t :documentation - "Whether the client implementation supports a refresh request sent from the + ((refresh-support :type lem-lsp-base/type:lsp-boolean :initarg :refresh-support :accessor + inline-value-workspace-client-capabilities-refresh-support :optional common-lisp:t + :documentation "Whether the client implementation supports a refresh request sent from the server to the client. Note that this event is global and will force the client to refresh all @@ -6202,9 +6301,8 @@ change that requires such a calculation.")) (lem-lsp-base/type:define-class inlay-hint-workspace-client-capabilities common-lisp:nil - ((refresh-support :type lem-lsp-base/type:lsp-boolean :initarg - :refresh-support :accessor inlay-hint-workspace-client-capabilities-refresh-support :optional - common-lisp:t :documentation + ((refresh-support :type lem-lsp-base/type:lsp-boolean :initarg :refresh-support :accessor + inlay-hint-workspace-client-capabilities-refresh-support :optional common-lisp:t :documentation "Whether the client implementation supports a refresh request sent from the server to the client. @@ -6219,9 +6317,8 @@ change that requires such a calculation.")) (lem-lsp-base/type:define-class diagnostic-workspace-client-capabilities common-lisp:nil - ((refresh-support :type lem-lsp-base/type:lsp-boolean :initarg - :refresh-support :accessor diagnostic-workspace-client-capabilities-refresh-support :optional - common-lisp:t :documentation + ((refresh-support :type lem-lsp-base/type:lsp-boolean :initarg :refresh-support :accessor + diagnostic-workspace-client-capabilities-refresh-support :optional common-lisp:t :documentation "Whether the client implementation supports a refresh request sent from the server to the client. @@ -6234,18 +6331,39 @@ change that requires such a calculation.")) @since 3.17.0")) +(lem-lsp-base/type:define-class folding-range-workspace-client-capabilities + common-lisp:nil + ((refresh-support :type lem-lsp-base/type:lsp-boolean :initarg :refresh-support :accessor + folding-range-workspace-client-capabilities-refresh-support :optional common-lisp:t :proposed + common-lisp:t :since "3.18.0" :documentation + "Whether the client implementation supports a refresh request sent from the +server to the client. + +Note that this event is global and will force the client to refresh all +folding ranges currently shown. It should be used with absolute care and is +useful for situation where a server for example detects a project wide +change that requires such a calculation. + +@since 3.18.0 +@proposed")) + (:proposed common-lisp:t) + (:since "3.18.0") + (:documentation "Client workspace capabilities specific to folding ranges + +@since 3.18.0 +@proposed")) + (lem-lsp-base/type:define-class text-document-sync-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor text-document-sync-client-capabilities-dynamic-registration - :optional common-lisp:t :documentation - "Whether text document synchronization supports dynamic registration.") + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor text-document-sync-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether text document synchronization supports dynamic registration.") (will-save :type lem-lsp-base/type:lsp-boolean :initarg :will-save :accessor text-document-sync-client-capabilities-will-save :optional common-lisp:t :documentation "The client supports sending will save notifications.") - (will-save-wait-until :type lem-lsp-base/type:lsp-boolean :initarg - :will-save-wait-until :accessor text-document-sync-client-capabilities-will-save-wait-until - :optional common-lisp:t :documentation "The client supports sending a will save request and + (will-save-wait-until :type lem-lsp-base/type:lsp-boolean :initarg :will-save-wait-until + :accessor text-document-sync-client-capabilities-will-save-wait-until :optional common-lisp:t + :documentation "The client supports sending a will save request and waits for a response providing text edits which will be applied to the document before it is saved.") (did-save :type lem-lsp-base/type:lsp-boolean :initarg :did-save :accessor @@ -6254,33 +6372,31 @@ be applied to the document before it is saved.") (lem-lsp-base/type:define-class completion-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor completion-client-capabilities-dynamic-registration :optional - common-lisp:t :documentation "Whether completion supports dynamic registration.") + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor completion-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether completion supports dynamic registration.") (completion-item :type (lem-lsp-base/type:lsp-interface - ((snippet-support :type lem-lsp-base/type:lsp-boolean :optional - common-lisp:t :documentation "Client supports snippets as insert text. + ((snippet-support :type lem-lsp-base/type:lsp-boolean :optional common-lisp:t :documentation + "Client supports snippets as insert text. A snippet can define tab stops and placeholders with `$1`, `$2` and `${3:foo}`. `$0` defines the final tab stop, it defaults to the end of the snippet. Placeholders with equal identifiers are linked, that is typing in one will update others too.") - (commit-characters-support :type lem-lsp-base/type:lsp-boolean :optional - common-lisp:t :documentation "Client supports commit characters on a completion item.") - (documentation-format :type (lem-lsp-base/type:lsp-array markup-kind) - :optional common-lisp:t :documentation - "Client supports the following content formats for the documentation + (commit-characters-support :type lem-lsp-base/type:lsp-boolean :optional common-lisp:t + :documentation "Client supports commit characters on a completion item.") + (documentation-format :type (lem-lsp-base/type:lsp-array markup-kind) :optional common-lisp:t + :documentation "Client supports the following content formats for the documentation property. The order describes the preferred format of the client.") - (deprecated-support :type lem-lsp-base/type:lsp-boolean :optional - common-lisp:t :documentation - "Client supports the deprecated property on a completion item.") - (preselect-support :type lem-lsp-base/type:lsp-boolean :optional - common-lisp:t :documentation "Client supports the preselect property on a completion item.") + (deprecated-support :type lem-lsp-base/type:lsp-boolean :optional common-lisp:t + :documentation "Client supports the deprecated property on a completion item.") + (preselect-support :type lem-lsp-base/type:lsp-boolean :optional common-lisp:t :documentation + "Client supports the preselect property on a completion item.") (tag-support :type (lem-lsp-base/type:lsp-interface - ((value-set :type (lem-lsp-base/type:lsp-array completion-item-tag) - :documentation "The tags supported by the client."))) + ((value-set :type (lem-lsp-base/type:lsp-array completion-item-tag) :documentation + "The tags supported by the client."))) :optional common-lisp:t :since "3.15.0" :documentation "Client supports the tag property on a completion item. Clients supporting tags have to handle unknown tags gracefully. Clients especially need to @@ -6288,17 +6404,15 @@ preserve unknown tags when sending a completion item back to the server in a resolve call. @since 3.15.0") - (insert-replace-support :type lem-lsp-base/type:lsp-boolean :optional - common-lisp:t :since "3.16.0" :documentation + (insert-replace-support :type lem-lsp-base/type:lsp-boolean :optional common-lisp:t :since + "3.16.0" :documentation "Client support insert replace edit to control different behavior if a completion item is inserted in the text or should replace text. @since 3.16.0") (resolve-support :type (lem-lsp-base/type:lsp-interface - ((properties :type - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-string) + ((properties :type (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-string) :documentation "The properties that a client can resolve lazily."))) :optional common-lisp:t :since "3.16.0" :documentation "Indicates which properties a client can resolve lazily on a completion @@ -6315,9 +6429,8 @@ a completion item to override the whitespace handling mode as defined by the client (see `insertTextMode`). @since 3.16.0") - (label-details-support :type lem-lsp-base/type:lsp-boolean :optional - common-lisp:t :since "3.17.0" :documentation - "The client has support for completion item label + (label-details-support :type lem-lsp-base/type:lsp-boolean :optional common-lisp:t :since + "3.17.0" :documentation "The client has support for completion item label details (see also `CompletionItemLabelDetails`). @since 3.17.0"))) @@ -6326,8 +6439,7 @@ details (see also `CompletionItemLabelDetails`). capabilities.") (completion-item-kind :type (lem-lsp-base/type:lsp-interface - ((value-set :type - (lem-lsp-base/type:lsp-array completion-item-kind) + ((value-set :type (lem-lsp-base/type:lsp-array completion-item-kind) :optional common-lisp:t :documentation "The completion item kind values the client supports. When this property exists the client also guarantees that it will @@ -6347,16 +6459,14 @@ when accepting a completion item that uses multi line text in either `insertText` or `textEdit`. @since 3.17.0") - (context-support :type lem-lsp-base/type:lsp-boolean :initarg - :context-support :accessor completion-client-capabilities-context-support :optional - common-lisp:t :documentation "The client supports to send additional context information for a + (context-support :type lem-lsp-base/type:lsp-boolean :initarg :context-support :accessor + completion-client-capabilities-context-support :optional common-lisp:t :documentation + "The client supports to send additional context information for a `textDocument/completion` request.") (completion-list :type (lem-lsp-base/type:lsp-interface - ((item-defaults :type - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-string) - :optional common-lisp:t :since "3.17.0" :documentation + ((item-defaults :type (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-string) :optional + common-lisp:t :since "3.17.0" :documentation "The client supports the following itemDefaults on a completion list. @@ -6375,37 +6485,35 @@ capabilities. (lem-lsp-base/type:define-class hover-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor hover-client-capabilities-dynamic-registration :optional - common-lisp:t :documentation "Whether hover supports dynamic registration.") - (content-format :type (lem-lsp-base/type:lsp-array markup-kind) :initarg - :content-format :accessor hover-client-capabilities-content-format :optional common-lisp:t - :documentation "Client supports the following content formats for the content + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor hover-client-capabilities-dynamic-registration :optional common-lisp:t :documentation + "Whether hover supports dynamic registration.") + (content-format :type (lem-lsp-base/type:lsp-array markup-kind) :initarg :content-format + :accessor hover-client-capabilities-content-format :optional common-lisp:t :documentation + "Client supports the following content formats for the content property. The order describes the preferred format of the client."))) (lem-lsp-base/type:define-class signature-help-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor signature-help-client-capabilities-dynamic-registration - :optional common-lisp:t :documentation "Whether signature help supports dynamic registration.") + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor signature-help-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether signature help supports dynamic registration.") (signature-information :type (lem-lsp-base/type:lsp-interface - ((documentation-format :type (lem-lsp-base/type:lsp-array markup-kind) - :optional common-lisp:t :documentation - "Client supports the following content formats for the documentation + ((documentation-format :type (lem-lsp-base/type:lsp-array markup-kind) :optional common-lisp:t + :documentation "Client supports the following content formats for the documentation property. The order describes the preferred format of the client.") (parameter-information :type (lem-lsp-base/type:lsp-interface - ((label-offset-support :type lem-lsp-base/type:lsp-boolean :optional - common-lisp:t :since "3.14.0" :documentation - "The client supports processing label offsets instead of a + ((label-offset-support :type lem-lsp-base/type:lsp-boolean :optional common-lisp:t :since + "3.14.0" :documentation "The client supports processing label offsets instead of a simple label string. @since 3.14.0"))) :optional common-lisp:t :documentation "Client capabilities specific to parameter information.") - (active-parameter-support :type lem-lsp-base/type:lsp-boolean :optional - common-lisp:t :since "3.16.0" :documentation + (active-parameter-support :type lem-lsp-base/type:lsp-boolean :optional common-lisp:t :since + "3.16.0" :documentation "The client supports the `activeParameter` property on `SignatureInformation` literal. @@ -6414,10 +6522,9 @@ literal. signature-help-client-capabilities-signature-information :optional common-lisp:t :documentation "The client supports the following `SignatureInformation` specific properties.") - (context-support :type lem-lsp-base/type:lsp-boolean :initarg - :context-support :accessor signature-help-client-capabilities-context-support :optional - common-lisp:t :since "3.15.0" :documentation - "The client supports to send additional context information for a + (context-support :type lem-lsp-base/type:lsp-boolean :initarg :context-support :accessor + signature-help-client-capabilities-context-support :optional common-lisp:t :since "3.15.0" + :documentation "The client supports to send additional context information for a `textDocument/signatureHelp` request. A client that opts into contextSupport will also support the `retriggerCharacters` on `SignatureHelpOptions`. @@ -6427,25 +6534,24 @@ contextSupport will also support the `retriggerCharacters` on (lem-lsp-base/type:define-class declaration-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor declaration-client-capabilities-dynamic-registration :optional - common-lisp:t :documentation - "Whether declaration supports dynamic registration. If this is set to `true` + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor declaration-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether declaration supports dynamic registration. If this is set to `true` the client supports the new `DeclarationRegistrationOptions` return value for the corresponding server capability as well.") - (link-support :type lem-lsp-base/type:lsp-boolean :initarg :link-support - :accessor declaration-client-capabilities-link-support :optional common-lisp:t :documentation + (link-support :type lem-lsp-base/type:lsp-boolean :initarg :link-support :accessor + declaration-client-capabilities-link-support :optional common-lisp:t :documentation "The client supports additional metadata in the form of declaration links.")) (:since "3.14.0") (:documentation "@since 3.14.0")) (lem-lsp-base/type:define-class definition-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor definition-client-capabilities-dynamic-registration :optional - common-lisp:t :documentation "Whether definition supports dynamic registration.") - (link-support :type lem-lsp-base/type:lsp-boolean :initarg :link-support - :accessor definition-client-capabilities-link-support :optional common-lisp:t :since "3.14.0" + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor definition-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether definition supports dynamic registration.") + (link-support :type lem-lsp-base/type:lsp-boolean :initarg :link-support :accessor + definition-client-capabilities-link-support :optional common-lisp:t :since "3.14.0" :documentation "The client supports additional metadata in the form of definition links. @since 3.14.0")) @@ -6453,31 +6559,28 @@ for the corresponding server capability as well.") (lem-lsp-base/type:define-class type-definition-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor type-definition-client-capabilities-dynamic-registration - :optional common-lisp:t :documentation - "Whether implementation supports dynamic registration. If this is set to `true` + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor type-definition-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether implementation supports dynamic registration. If this is set to `true` the client supports the new `TypeDefinitionRegistrationOptions` return value for the corresponding server capability as well.") - (link-support :type lem-lsp-base/type:lsp-boolean :initarg :link-support - :accessor type-definition-client-capabilities-link-support :optional common-lisp:t - :documentation "The client supports additional metadata in the form of definition links. + (link-support :type lem-lsp-base/type:lsp-boolean :initarg :link-support :accessor + type-definition-client-capabilities-link-support :optional common-lisp:t :documentation + "The client supports additional metadata in the form of definition links. Since 3.14.0")) (:documentation "Since 3.6.0")) (lem-lsp-base/type:define-class implementation-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor implementation-client-capabilities-dynamic-registration - :optional common-lisp:t :documentation - "Whether implementation supports dynamic registration. If this is set to `true` + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor implementation-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether implementation supports dynamic registration. If this is set to `true` the client supports the new `ImplementationRegistrationOptions` return value for the corresponding server capability as well.") - (link-support :type lem-lsp-base/type:lsp-boolean :initarg :link-support - :accessor implementation-client-capabilities-link-support :optional common-lisp:t :since - "3.14.0" :documentation - "The client supports additional metadata in the form of definition links. + (link-support :type lem-lsp-base/type:lsp-boolean :initarg :link-support :accessor + implementation-client-capabilities-link-support :optional common-lisp:t :since "3.14.0" + :documentation "The client supports additional metadata in the form of definition links. @since 3.14.0")) (:since "3.6.0") @@ -6485,29 +6588,27 @@ for the corresponding server capability as well.") (lem-lsp-base/type:define-class reference-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor reference-client-capabilities-dynamic-registration :optional - common-lisp:t :documentation "Whether references supports dynamic registration.")) + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor reference-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether references supports dynamic registration.")) (:documentation "Client Capabilities for a {@link ReferencesRequest}.")) (lem-lsp-base/type:define-class document-highlight-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor document-highlight-client-capabilities-dynamic-registration - :optional common-lisp:t :documentation - "Whether document highlight supports dynamic registration.")) + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor document-highlight-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether document highlight supports dynamic registration.")) (:documentation "Client Capabilities for a {@link DocumentHighlightRequest}.")) (lem-lsp-base/type:define-class document-symbol-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor document-symbol-client-capabilities-dynamic-registration - :optional common-lisp:t :documentation - "Whether document symbol supports dynamic registration.") + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor document-symbol-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether document symbol supports dynamic registration.") (symbol-kind :type (lem-lsp-base/type:lsp-interface - ((value-set :type (lem-lsp-base/type:lsp-array symbol-kind) :optional - common-lisp:t :documentation "The symbol kind values the client supports. When this + ((value-set :type (lem-lsp-base/type:lsp-array symbol-kind) :optional common-lisp:t + :documentation "The symbol kind values the client supports. When this property exists the client also guarantees that it will handle values outside its set gracefully and falls back to a default value when unknown. @@ -6518,8 +6619,8 @@ the initial version of the protocol."))) :initarg :symbol-kind :accessor document-symbol-client-capabilities-symbol-kind :optional common-lisp:t :documentation "Specific capabilities for the `SymbolKind` in the `textDocument/documentSymbol` request.") - (hierarchical-document-symbol-support :type lem-lsp-base/type:lsp-boolean - :initarg :hierarchical-document-symbol-support :accessor + (hierarchical-document-symbol-support :type lem-lsp-base/type:lsp-boolean :initarg + :hierarchical-document-symbol-support :accessor document-symbol-client-capabilities-hierarchical-document-symbol-support :optional common-lisp:t :documentation "The client supports hierarchical document symbols.") (tag-support :type @@ -6533,9 +6634,9 @@ the initial version of the protocol."))) Clients supporting tags have to handle unknown tags gracefully. @since 3.16.0") - (label-support :type lem-lsp-base/type:lsp-boolean :initarg :label-support - :accessor document-symbol-client-capabilities-label-support :optional common-lisp:t :since - "3.16.0" :documentation "The client supports an additional label presented in the UI when + (label-support :type lem-lsp-base/type:lsp-boolean :initarg :label-support :accessor + document-symbol-client-capabilities-label-support :optional common-lisp:t :since "3.16.0" + :documentation "The client supports an additional label presented in the UI when registering a document symbol provider. @since 3.16.0")) @@ -6543,15 +6644,14 @@ registering a document symbol provider. (lem-lsp-base/type:define-class code-action-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor code-action-client-capabilities-dynamic-registration :optional - common-lisp:t :documentation "Whether code action supports dynamic registration.") + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor code-action-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether code action supports dynamic registration.") (code-action-literal-support :type (lem-lsp-base/type:lsp-interface ((code-action-kind :type (lem-lsp-base/type:lsp-interface - ((value-set :type - (lem-lsp-base/type:lsp-array code-action-kind) + ((value-set :type (lem-lsp-base/type:lsp-array code-action-kind) :documentation "The code action kind values the client supports. When this property exists the client also guarantees that it will @@ -6566,20 +6666,18 @@ response of the `textDocument/codeAction` request. If the property is not set the request can only return `Command` literals. @since 3.8.0") - (is-preferred-support :type lem-lsp-base/type:lsp-boolean :initarg - :is-preferred-support :accessor code-action-client-capabilities-is-preferred-support :optional - common-lisp:t :since "3.15.0" :documentation - "Whether code action supports the `isPreferred` property. + (is-preferred-support :type lem-lsp-base/type:lsp-boolean :initarg :is-preferred-support + :accessor code-action-client-capabilities-is-preferred-support :optional common-lisp:t :since + "3.15.0" :documentation "Whether code action supports the `isPreferred` property. @since 3.15.0") - (disabled-support :type lem-lsp-base/type:lsp-boolean :initarg - :disabled-support :accessor code-action-client-capabilities-disabled-support :optional - common-lisp:t :since "3.16.0" :documentation - "Whether code action supports the `disabled` property. + (disabled-support :type lem-lsp-base/type:lsp-boolean :initarg :disabled-support :accessor + code-action-client-capabilities-disabled-support :optional common-lisp:t :since "3.16.0" + :documentation "Whether code action supports the `disabled` property. @since 3.16.0") - (data-support :type lem-lsp-base/type:lsp-boolean :initarg :data-support - :accessor code-action-client-capabilities-data-support :optional common-lisp:t :since "3.16.0" + (data-support :type lem-lsp-base/type:lsp-boolean :initarg :data-support :accessor + code-action-client-capabilities-data-support :optional common-lisp:t :since "3.16.0" :documentation "Whether code action supports the `data` property which is preserved between a `textDocument/codeAction` and a `codeAction/resolve` request. @@ -6587,10 +6685,8 @@ preserved between a `textDocument/codeAction` and a @since 3.16.0") (resolve-support :type (lem-lsp-base/type:lsp-interface - ((properties :type - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-string) - :documentation "The properties that a client can resolve lazily."))) + ((properties :type (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-string) :documentation + "The properties that a client can resolve lazily."))) :initarg :resolve-support :accessor code-action-client-capabilities-resolve-support :optional common-lisp:t :since "3.16.0" :documentation "Whether the client supports resolving additional code action @@ -6611,64 +6707,67 @@ for confirmation. (lem-lsp-base/type:define-class code-lens-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor code-lens-client-capabilities-dynamic-registration :optional - common-lisp:t :documentation "Whether code lens supports dynamic registration.")) + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor code-lens-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether code lens supports dynamic registration.")) (:documentation "The client capabilities of a {@link CodeLensRequest}.")) (lem-lsp-base/type:define-class document-link-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor document-link-client-capabilities-dynamic-registration - :optional common-lisp:t :documentation "Whether document link supports dynamic registration.") - (tooltip-support :type lem-lsp-base/type:lsp-boolean :initarg - :tooltip-support :accessor document-link-client-capabilities-tooltip-support :optional - common-lisp:t :since "3.15.0" :documentation - "Whether the client supports the `tooltip` property on `DocumentLink`. + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor document-link-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether document link supports dynamic registration.") + (tooltip-support :type lem-lsp-base/type:lsp-boolean :initarg :tooltip-support :accessor + document-link-client-capabilities-tooltip-support :optional common-lisp:t :since "3.15.0" + :documentation "Whether the client supports the `tooltip` property on `DocumentLink`. @since 3.15.0")) (:documentation "The client capabilities of a {@link DocumentLinkRequest}.")) (lem-lsp-base/type:define-class document-color-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor document-color-client-capabilities-dynamic-registration - :optional common-lisp:t :documentation - "Whether implementation supports dynamic registration. If this is set to `true` + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor document-color-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether implementation supports dynamic registration. If this is set to `true` the client supports the new `DocumentColorRegistrationOptions` return value for the corresponding server capability as well."))) (lem-lsp-base/type:define-class document-formatting-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor document-formatting-client-capabilities-dynamic-registration - :optional common-lisp:t :documentation "Whether formatting supports dynamic registration.")) + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor document-formatting-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether formatting supports dynamic registration.")) (:documentation "Client capabilities of a {@link DocumentFormattingRequest}.")) (lem-lsp-base/type:define-class document-range-formatting-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor - document-range-formatting-client-capabilities-dynamic-registration :optional common-lisp:t - :documentation "Whether range formatting supports dynamic registration.")) + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor document-range-formatting-client-capabilities-dynamic-registration :optional + common-lisp:t :documentation "Whether range formatting supports dynamic registration.") + (ranges-support :type lem-lsp-base/type:lsp-boolean :initarg :ranges-support :accessor + document-range-formatting-client-capabilities-ranges-support :optional common-lisp:t :proposed + common-lisp:t :since "3.18.0" :documentation + "Whether the client supports formatting multiple ranges at once. + +@since 3.18.0 +@proposed")) (:documentation "Client capabilities of a {@link DocumentRangeFormattingRequest}.")) (lem-lsp-base/type:define-class document-on-type-formatting-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor - document-on-type-formatting-client-capabilities-dynamic-registration :optional common-lisp:t - :documentation "Whether on type formatting supports dynamic registration.")) + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor document-on-type-formatting-client-capabilities-dynamic-registration :optional + common-lisp:t :documentation "Whether on type formatting supports dynamic registration.")) (:documentation "Client capabilities of a {@link DocumentOnTypeFormattingRequest}.")) (lem-lsp-base/type:define-class rename-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor rename-client-capabilities-dynamic-registration :optional - common-lisp:t :documentation "Whether rename supports dynamic registration.") - (prepare-support :type lem-lsp-base/type:lsp-boolean :initarg - :prepare-support :accessor rename-client-capabilities-prepare-support :optional common-lisp:t - :since "3.12.0" :documentation "Client supports testing for validity of rename operations + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor rename-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether rename supports dynamic registration.") + (prepare-support :type lem-lsp-base/type:lsp-boolean :initarg :prepare-support :accessor + rename-client-capabilities-prepare-support :optional common-lisp:t :since "3.12.0" + :documentation "Client supports testing for validity of rename operations before execution. @since 3.12.0") @@ -6694,28 +6793,25 @@ for confirmation. (lem-lsp-base/type:define-class folding-range-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor folding-range-client-capabilities-dynamic-registration - :optional common-lisp:t :documentation - "Whether implementation supports dynamic registration for folding range + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor folding-range-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether implementation supports dynamic registration for folding range providers. If this is set to `true` the client supports the new `FoldingRangeRegistrationOptions` return value for the corresponding server capability as well.") - (range-limit :type lem-lsp-base/type:lsp-uinteger :initarg :range-limit - :accessor folding-range-client-capabilities-range-limit :optional common-lisp:t :documentation + (range-limit :type lem-lsp-base/type:lsp-uinteger :initarg :range-limit :accessor + folding-range-client-capabilities-range-limit :optional common-lisp:t :documentation "The maximum number of folding ranges that the client prefers to receive per document. The value serves as a hint, servers are free to follow the limit.") - (line-folding-only :type lem-lsp-base/type:lsp-boolean :initarg - :line-folding-only :accessor folding-range-client-capabilities-line-folding-only :optional - common-lisp:t :documentation + (line-folding-only :type lem-lsp-base/type:lsp-boolean :initarg :line-folding-only :accessor + folding-range-client-capabilities-line-folding-only :optional common-lisp:t :documentation "If set, the client signals that it only supports folding complete lines. If set, client will ignore specified `startCharacter` and `endCharacter` properties in a FoldingRange.") (folding-range-kind :type (lem-lsp-base/type:lsp-interface - ((value-set :type - (lem-lsp-base/type:lsp-array folding-range-kind) + ((value-set :type (lem-lsp-base/type:lsp-array folding-range-kind) :optional common-lisp:t :documentation "The folding range kind values the client supports. When this property exists the client also guarantees that it will @@ -6728,9 +6824,8 @@ to a default value when unknown."))) @since 3.17.0") (folding-range :type (lem-lsp-base/type:lsp-interface - ((collapsed-text :type lem-lsp-base/type:lsp-boolean :optional - common-lisp:t :since "3.17.0" :documentation - "If set, the client signals that it supports setting collapsedText on + ((collapsed-text :type lem-lsp-base/type:lsp-boolean :optional common-lisp:t :since "3.17.0" + :documentation "If set, the client signals that it supports setting collapsedText on folding ranges to display custom labels instead of the default text. @since 3.17.0"))) @@ -6741,45 +6836,42 @@ folding ranges to display custom labels instead of the default text. (lem-lsp-base/type:define-class selection-range-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor selection-range-client-capabilities-dynamic-registration - :optional common-lisp:t :documentation + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor selection-range-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether implementation supports dynamic registration for selection range providers. If this is set to `true` the client supports the new `SelectionRangeRegistrationOptions` return value for the corresponding server capability as well."))) (lem-lsp-base/type:define-class publish-diagnostics-client-capabilities common-lisp:nil - ((related-information :type lem-lsp-base/type:lsp-boolean :initarg - :related-information :accessor publish-diagnostics-client-capabilities-related-information - :optional common-lisp:t :documentation - "Whether the clients accepts diagnostics with related information.") + ((related-information :type lem-lsp-base/type:lsp-boolean :initarg :related-information :accessor + publish-diagnostics-client-capabilities-related-information :optional common-lisp:t + :documentation "Whether the clients accepts diagnostics with related information.") (tag-support :type (lem-lsp-base/type:lsp-interface - ((value-set :type (lem-lsp-base/type:lsp-array diagnostic-tag) - :documentation "The tags supported by the client."))) + ((value-set :type (lem-lsp-base/type:lsp-array diagnostic-tag) :documentation + "The tags supported by the client."))) :initarg :tag-support :accessor publish-diagnostics-client-capabilities-tag-support :optional common-lisp:t :since "3.15.0" :documentation "Client supports the tag property to provide meta data about a diagnostic. Clients supporting tags have to handle unknown tags gracefully. @since 3.15.0") - (version-support :type lem-lsp-base/type:lsp-boolean :initarg - :version-support :accessor publish-diagnostics-client-capabilities-version-support :optional - common-lisp:t :since "3.15.0" :documentation - "Whether the client interprets the version property of the + (version-support :type lem-lsp-base/type:lsp-boolean :initarg :version-support :accessor + publish-diagnostics-client-capabilities-version-support :optional common-lisp:t :since "3.15.0" + :documentation "Whether the client interprets the version property of the `textDocument/publishDiagnostics` notification's parameter. @since 3.15.0") - (code-description-support :type lem-lsp-base/type:lsp-boolean :initarg - :code-description-support :accessor - publish-diagnostics-client-capabilities-code-description-support :optional common-lisp:t :since - "3.16.0" :documentation "Client supports a codeDescription property + (code-description-support :type lem-lsp-base/type:lsp-boolean :initarg :code-description-support + :accessor publish-diagnostics-client-capabilities-code-description-support :optional + common-lisp:t :since "3.16.0" :documentation "Client supports a codeDescription property @since 3.16.0") - (data-support :type lem-lsp-base/type:lsp-boolean :initarg :data-support - :accessor publish-diagnostics-client-capabilities-data-support :optional common-lisp:t :since - "3.16.0" :documentation "Whether code action supports the `data` property which is + (data-support :type lem-lsp-base/type:lsp-boolean :initarg :data-support :accessor + publish-diagnostics-client-capabilities-data-support :optional common-lisp:t :since "3.16.0" + :documentation "Whether code action supports the `data` property which is preserved between a `textDocument/publishDiagnostics` and `textDocument/codeAction` request. @@ -6788,10 +6880,9 @@ preserved between a `textDocument/publishDiagnostics` and (lem-lsp-base/type:define-class call-hierarchy-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor call-hierarchy-client-capabilities-dynamic-registration - :optional common-lisp:t :documentation - "Whether implementation supports dynamic registration. If this is set to `true` + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor call-hierarchy-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether implementation supports dynamic registration. If this is set to `true` the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` return value for the corresponding server capability as well.")) (:since "3.16.0") @@ -6799,10 +6890,9 @@ return value for the corresponding server capability as well.")) (lem-lsp-base/type:define-class semantic-tokens-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor semantic-tokens-client-capabilities-dynamic-registration - :optional common-lisp:t :documentation - "Whether implementation supports dynamic registration. If this is set to `true` + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor semantic-tokens-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether implementation supports dynamic registration. If this is set to `true` the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` return value for the corresponding server capability as well.") (requests :type @@ -6816,8 +6906,8 @@ the server provides a corresponding handler.") (full :type (common-lisp:or lem-lsp-base/type:lsp-boolean (lem-lsp-base/type:lsp-interface - ((delta :type lem-lsp-base/type:lsp-boolean :optional - common-lisp:t :documentation + ((delta :type lem-lsp-base/type:lsp-boolean :optional common-lisp:t + :documentation "The client will send the `textDocument/semanticTokens/full/delta` request if the server provides a corresponding handler.")))) :optional common-lisp:t :documentation @@ -6832,40 +6922,33 @@ server. If for example the client capability `requests.full` and `request.range` are both set to true but the server only provides a range provider the client might not render a minimap correctly or might even decide to not show any semantic tokens at all.") - (token-types :type - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-string) - :initarg :token-types :accessor semantic-tokens-client-capabilities-token-types :documentation + (token-types :type (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-string) :initarg + :token-types :accessor semantic-tokens-client-capabilities-token-types :documentation "The token types that the client supports.") - (token-modifiers :type - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-string) - :initarg :token-modifiers :accessor semantic-tokens-client-capabilities-token-modifiers - :documentation "The token modifiers that the client supports.") - (formats :type (lem-lsp-base/type:lsp-array token-format) :initarg :formats - :accessor semantic-tokens-client-capabilities-formats :documentation + (token-modifiers :type (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-string) :initarg + :token-modifiers :accessor semantic-tokens-client-capabilities-token-modifiers :documentation + "The token modifiers that the client supports.") + (formats :type (lem-lsp-base/type:lsp-array token-format) :initarg :formats :accessor + semantic-tokens-client-capabilities-formats :documentation "The token formats the clients supports.") (overlapping-token-support :type lem-lsp-base/type:lsp-boolean :initarg :overlapping-token-support :accessor semantic-tokens-client-capabilities-overlapping-token-support :optional common-lisp:t :documentation "Whether the client supports tokens that can overlap each other.") - (multiline-token-support :type lem-lsp-base/type:lsp-boolean :initarg - :multiline-token-support :accessor semantic-tokens-client-capabilities-multiline-token-support - :optional common-lisp:t :documentation - "Whether the client supports tokens that can span multiple lines.") - (server-cancel-support :type lem-lsp-base/type:lsp-boolean :initarg - :server-cancel-support :accessor semantic-tokens-client-capabilities-server-cancel-support - :optional common-lisp:t :since "3.17.0" :documentation - "Whether the client allows the server to actively cancel a + (multiline-token-support :type lem-lsp-base/type:lsp-boolean :initarg :multiline-token-support + :accessor semantic-tokens-client-capabilities-multiline-token-support :optional common-lisp:t + :documentation "Whether the client supports tokens that can span multiple lines.") + (server-cancel-support :type lem-lsp-base/type:lsp-boolean :initarg :server-cancel-support + :accessor semantic-tokens-client-capabilities-server-cancel-support :optional common-lisp:t + :since "3.17.0" :documentation "Whether the client allows the server to actively cancel a semantic token request, e.g. supports returning LSPErrorCodes.ServerCancelled. If a server does the client needs to retrigger the request. @since 3.17.0") - (augments-syntax-tokens :type lem-lsp-base/type:lsp-boolean :initarg - :augments-syntax-tokens :accessor semantic-tokens-client-capabilities-augments-syntax-tokens - :optional common-lisp:t :since "3.17.0" :documentation - "Whether the client uses semantic tokens to augment existing + (augments-syntax-tokens :type lem-lsp-base/type:lsp-boolean :initarg :augments-syntax-tokens + :accessor semantic-tokens-client-capabilities-augments-syntax-tokens :optional common-lisp:t + :since "3.17.0" :documentation "Whether the client uses semantic tokens to augment existing syntax tokens. If set to `true` client side created syntax tokens and semantic tokens are both used for colorization. If set to `false` the client only uses the returned semantic tokens @@ -6880,10 +6963,9 @@ specified. (lem-lsp-base/type:define-class linked-editing-range-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor linked-editing-range-client-capabilities-dynamic-registration - :optional common-lisp:t :documentation - "Whether implementation supports dynamic registration. If this is set to `true` + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor linked-editing-range-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether implementation supports dynamic registration. If this is set to `true` the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` return value for the corresponding server capability as well.")) (:since "3.16.0") @@ -6893,10 +6975,9 @@ return value for the corresponding server capability as well.")) (lem-lsp-base/type:define-class moniker-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor moniker-client-capabilities-dynamic-registration :optional - common-lisp:t :documentation - "Whether moniker supports dynamic registration. If this is set to `true` + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor moniker-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether moniker supports dynamic registration. If this is set to `true` the client supports the new `MonikerRegistrationOptions` return value for the corresponding server capability as well.")) (:since "3.16.0") @@ -6906,10 +6987,9 @@ for the corresponding server capability as well.")) (lem-lsp-base/type:define-class type-hierarchy-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor type-hierarchy-client-capabilities-dynamic-registration - :optional common-lisp:t :documentation - "Whether implementation supports dynamic registration. If this is set to `true` + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor type-hierarchy-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether implementation supports dynamic registration. If this is set to `true` the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` return value for the corresponding server capability as well.")) (:since "3.17.0") @@ -6917,9 +6997,9 @@ return value for the corresponding server capability as well.")) (lem-lsp-base/type:define-class inline-value-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor inline-value-client-capabilities-dynamic-registration :optional - common-lisp:t :documentation + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor inline-value-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether implementation supports dynamic registration for inline value providers.")) (:since "3.17.0") (:documentation "Client capabilities specific to inline values. @@ -6928,15 +7008,13 @@ return value for the corresponding server capability as well.")) (lem-lsp-base/type:define-class inlay-hint-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor inlay-hint-client-capabilities-dynamic-registration :optional - common-lisp:t :documentation "Whether inlay hints support dynamic registration.") + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor inlay-hint-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether inlay hints support dynamic registration.") (resolve-support :type (lem-lsp-base/type:lsp-interface - ((properties :type - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-string) - :documentation "The properties that a client can resolve lazily."))) + ((properties :type (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-string) :documentation + "The properties that a client can resolve lazily."))) :initarg :resolve-support :accessor inlay-hint-client-capabilities-resolve-support :optional common-lisp:t :documentation "Indicates which properties a client can resolve lazily on an inlay hint.")) @@ -6947,27 +7025,37 @@ hint.")) (lem-lsp-base/type:define-class diagnostic-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor diagnostic-client-capabilities-dynamic-registration :optional - common-lisp:t :documentation - "Whether implementation supports dynamic registration. If this is set to `true` + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor diagnostic-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation "Whether implementation supports dynamic registration. If this is set to `true` the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` return value for the corresponding server capability as well.") - (related-document-support :type lem-lsp-base/type:lsp-boolean :initarg - :related-document-support :accessor diagnostic-client-capabilities-related-document-support - :optional common-lisp:t :documentation - "Whether the clients supports related documents for document diagnostic pulls.")) + (related-document-support :type lem-lsp-base/type:lsp-boolean :initarg :related-document-support + :accessor diagnostic-client-capabilities-related-document-support :optional common-lisp:t + :documentation "Whether the clients supports related documents for document diagnostic pulls.")) (:since "3.17.0") (:documentation "Client capabilities specific to diagnostic pull requests. @since 3.17.0")) +(lem-lsp-base/type:define-class inline-completion-client-capabilities + common-lisp:nil + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor inline-completion-client-capabilities-dynamic-registration :optional common-lisp:t + :documentation + "Whether implementation supports dynamic registration for inline completion providers.")) + (:proposed common-lisp:t) + (:since "3.18.0") + (:documentation "Client capabilities specific to inline completions. + +@since 3.18.0 +@proposed")) + (lem-lsp-base/type:define-class notebook-document-sync-client-capabilities common-lisp:nil - ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg - :dynamic-registration :accessor notebook-document-sync-client-capabilities-dynamic-registration - :optional common-lisp:t :documentation - "Whether implementation supports dynamic registration. If this is + ((dynamic-registration :type lem-lsp-base/type:lsp-boolean :initarg :dynamic-registration + :accessor notebook-document-sync-client-capabilities-dynamic-registration :optional + common-lisp:t :documentation "Whether implementation supports dynamic registration. If this is set to `true` the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` return value for the corresponding server capability as well.") @@ -6984,9 +7072,8 @@ return value for the corresponding server capability as well.") common-lisp:nil ((message-action-item :type (lem-lsp-base/type:lsp-interface - ((additional-properties-support :type lem-lsp-base/type:lsp-boolean - :optional common-lisp:t :documentation - "Whether the client supports additional attributes which + ((additional-properties-support :type lem-lsp-base/type:lsp-boolean :optional common-lisp:t + :documentation "Whether the client supports additional attributes which are preserved and send back to the server in the request's response."))) :initarg :message-action-item :accessor @@ -7024,12 +7111,9 @@ request.")) (version :type lem-lsp-base/type:lsp-string :initarg :version :accessor markdown-client-capabilities-version :optional common-lisp:t :documentation "The version of the parser.") - (allowed-tags :type - (lem-lsp-base/type:lsp-array - lem-lsp-base/type:lsp-string) - :initarg :allowed-tags :accessor markdown-client-capabilities-allowed-tags :optional - common-lisp:t :since "3.17.0" :documentation - "A list of HTML tags that the client allows / supports in + (allowed-tags :type (lem-lsp-base/type:lsp-array lem-lsp-base/type:lsp-string) :initarg + :allowed-tags :accessor markdown-client-capabilities-allowed-tags :optional common-lisp:t + :since "3.17.0" :documentation "A list of HTML tags that the client allows / supports in Markdown. @since 3.17.0")) @@ -7061,12 +7145,9 @@ the defining symbol")) (:since "3.17.0")) (lem-lsp-base/type:define-type-alias lsp-any - (common-lisp:or lsp-object lsp-array lem-lsp-base/type:lsp-string - lem-lsp-base/type:lsp-integer - lem-lsp-base/type:lsp-uinteger - lem-lsp-base/type:lsp-decimal - lem-lsp-base/type:lsp-boolean - lem-lsp-base/type:lsp-null) + (common-lisp:or lsp-object lsp-array lem-lsp-base/type:lsp-string lem-lsp-base/type:lsp-integer + lem-lsp-base/type:lsp-uinteger lem-lsp-base/type:lsp-decimal + lem-lsp-base/type:lsp-boolean lem-lsp-base/type:lsp-null) (:documentation "The LSP any type. Please note that strictly speaking a property with the value `undefined` can't be converted into JSON preserving the property name. However for @@ -7117,8 +7198,7 @@ pull request. (lem-lsp-base/type:define-type-alias prepare-rename-result (common-lisp:or range (lem-lsp-base/type:lsp-interface - ((range :type range) - (placeholder :type lem-lsp-base/type:lsp-string))) + ((range :type range) (placeholder :type lem-lsp-base/type:lsp-string))) (lem-lsp-base/type:lsp-interface ((default-behavior :type lem-lsp-base/type:lsp-boolean))))) @@ -7132,8 +7212,7 @@ The use of a string as a document filter is deprecated @since 3.16.0.") (:since "3.16.0.")) (lem-lsp-base/type:define-type-alias progress-token - (common-lisp:or lem-lsp-base/type:lsp-integer - lem-lsp-base/type:lsp-string)) + (common-lisp:or lem-lsp-base/type:lsp-integer lem-lsp-base/type:lsp-string)) (lem-lsp-base/type:define-type-alias change-annotation-identifier lem-lsp-base/type:lsp-string @@ -7151,8 +7230,8 @@ The use of a string as a document filter is deprecated @since 3.16.0.") (common-lisp:or (lem-lsp-base/type:lsp-interface ((range :type range :documentation "The range of the document that changed.") - (range-length :type lem-lsp-base/type:lsp-uinteger :optional - common-lisp:t :documentation "The optional length of the range that got replaced. + (range-length :type lem-lsp-base/type:lsp-uinteger :optional common-lisp:t :documentation + "The optional length of the range that got replaced. @deprecated use range instead.") (text :type lem-lsp-base/type:lsp-string :documentation @@ -7209,24 +7288,24 @@ a notebook cell document. (lem-lsp-base/type:lsp-interface ((language :type lem-lsp-base/type:lsp-string :documentation "A language id, like `typescript`.") - (scheme :type lem-lsp-base/type:lsp-string :optional common-lisp:t - :documentation "A Uri {@link Uri.scheme scheme}, like `file` or `untitled`.") - (pattern :type lem-lsp-base/type:lsp-string :optional common-lisp:t - :documentation "A glob pattern, like `*.{ts,js}`."))) + (scheme :type lem-lsp-base/type:lsp-string :optional common-lisp:t :documentation + "A Uri {@link Uri.scheme scheme}, like `file` or `untitled`.") + (pattern :type lem-lsp-base/type:lsp-string :optional common-lisp:t :documentation + "A glob pattern, like **​/*.{ts,js}. See TextDocumentFilter for examples."))) (lem-lsp-base/type:lsp-interface - ((language :type lem-lsp-base/type:lsp-string :optional common-lisp:t - :documentation "A language id, like `typescript`.") + ((language :type lem-lsp-base/type:lsp-string :optional common-lisp:t :documentation + "A language id, like `typescript`.") (scheme :type lem-lsp-base/type:lsp-string :documentation "A Uri {@link Uri.scheme scheme}, like `file` or `untitled`.") - (pattern :type lem-lsp-base/type:lsp-string :optional common-lisp:t - :documentation "A glob pattern, like `*.{ts,js}`."))) + (pattern :type lem-lsp-base/type:lsp-string :optional common-lisp:t :documentation + "A glob pattern, like **​/*.{ts,js}. See TextDocumentFilter for examples."))) (lem-lsp-base/type:lsp-interface - ((language :type lem-lsp-base/type:lsp-string :optional common-lisp:t - :documentation "A language id, like `typescript`.") - (scheme :type lem-lsp-base/type:lsp-string :optional common-lisp:t - :documentation "A Uri {@link Uri.scheme scheme}, like `file` or `untitled`.") + ((language :type lem-lsp-base/type:lsp-string :optional common-lisp:t :documentation + "A language id, like `typescript`.") + (scheme :type lem-lsp-base/type:lsp-string :optional common-lisp:t :documentation + "A Uri {@link Uri.scheme scheme}, like `file` or `untitled`.") (pattern :type lem-lsp-base/type:lsp-string :documentation - "A glob pattern, like `*.{ts,js}`.")))) + "A glob pattern, like **​/*.{ts,js}. See TextDocumentFilter for examples.")))) (:documentation "A document filter denotes a document by different properties like the {@link TextDocument.languageId language}, the {@link Uri.scheme scheme} of its resource, or a glob-pattern that is applied to the {@link TextDocument.fileName path}. @@ -7250,24 +7329,23 @@ Glob patterns can have the following syntax: (lem-lsp-base/type:lsp-interface ((notebook-type :type lem-lsp-base/type:lsp-string :documentation "The type of the enclosing notebook.") - (scheme :type lem-lsp-base/type:lsp-string :optional common-lisp:t - :documentation "A Uri {@link Uri.scheme scheme}, like `file` or `untitled`.") - (pattern :type lem-lsp-base/type:lsp-string :optional common-lisp:t - :documentation "A glob pattern."))) + (scheme :type lem-lsp-base/type:lsp-string :optional common-lisp:t :documentation + "A Uri {@link Uri.scheme scheme}, like `file` or `untitled`.") + (pattern :type lem-lsp-base/type:lsp-string :optional common-lisp:t :documentation + "A glob pattern."))) (lem-lsp-base/type:lsp-interface - ((notebook-type :type lem-lsp-base/type:lsp-string :optional - common-lisp:t :documentation "The type of the enclosing notebook.") + ((notebook-type :type lem-lsp-base/type:lsp-string :optional common-lisp:t :documentation + "The type of the enclosing notebook.") (scheme :type lem-lsp-base/type:lsp-string :documentation "A Uri {@link Uri.scheme scheme}, like `file` or `untitled`.") - (pattern :type lem-lsp-base/type:lsp-string :optional common-lisp:t - :documentation "A glob pattern."))) + (pattern :type lem-lsp-base/type:lsp-string :optional common-lisp:t :documentation + "A glob pattern."))) (lem-lsp-base/type:lsp-interface - ((notebook-type :type lem-lsp-base/type:lsp-string :optional - common-lisp:t :documentation "The type of the enclosing notebook.") - (scheme :type lem-lsp-base/type:lsp-string :optional common-lisp:t - :documentation "A Uri {@link Uri.scheme scheme}, like `file` or `untitled`.") - (pattern :type lem-lsp-base/type:lsp-string :documentation - "A glob pattern.")))) + ((notebook-type :type lem-lsp-base/type:lsp-string :optional common-lisp:t :documentation + "The type of the enclosing notebook.") + (scheme :type lem-lsp-base/type:lsp-string :optional common-lisp:t :documentation + "A Uri {@link Uri.scheme scheme}, like `file` or `untitled`.") + (pattern :type lem-lsp-base/type:lsp-string :documentation "A glob pattern.")))) (:documentation "A notebook document filter denotes a notebook document by different properties. The properties will be match against the notebook's URI (same as with documents) @@ -7295,9 +7373,8 @@ against the notebook's URI (same as with documents) common-lisp:nil :documentation "A request to resolve the implementation locations of a symbol at a given text -document position. The request's parameter is of type [TextDocumentPositionParams] -(#TextDocumentPositionParams) the response is of type {@link Definition} or a -Thenable that resolves to such." +document position. The request's parameter is of type {@link TextDocumentPositionParams} +the response is of type {@link Definition} or a Thenable that resolves to such." :error-data 'common-lisp:nil :message-direction @@ -7327,9 +7404,8 @@ Thenable that resolves to such." common-lisp:nil :documentation "A request to resolve the type definition locations of a symbol at a given text -document position. The request's parameter is of type [TextDocumentPositionParams] -(#TextDocumentPositionParams) the response is of type {@link Definition} or a -Thenable that resolves to such." +document position. The request's parameter is of type {@link TextDocumentPositionParams} +the response is of type {@link Definition} or a Thenable that resolves to such." :error-data 'common-lisp:nil :message-direction @@ -7376,8 +7452,7 @@ Thenable that resolves to such." :registration-options 'common-lisp:nil :result - '(common-lisp:or (lem-lsp-base/type:lsp-array workspace-folder) - lem-lsp-base/type:lsp-null) + '(common-lisp:or (lem-lsp-base/type:lsp-array workspace-folder) lem-lsp-base/type:lsp-null) :since common-lisp:nil) @@ -7389,7 +7464,7 @@ Thenable that resolves to such." "The 'workspace/configuration' request is sent from the server to the client to fetch a certain configuration setting. -This pull model replaces the old push model were the client signaled configuration change via an +This pull model replaces the old push model where the client signaled configuration change via an event. If the server still needs to react to configuration changes (since the server caches the result of `workspace/configuration` requests) the server should register for an empty configuration change event and empty the cache if such an event is received." @@ -7500,21 +7575,47 @@ that resolves to such." :registration-options 'folding-range-registration-options :result - '(common-lisp:or (lem-lsp-base/type:lsp-array folding-range) - lem-lsp-base/type:lsp-null) + '(common-lisp:or (lem-lsp-base/type:lsp-array folding-range) lem-lsp-base/type:lsp-null) :since common-lisp:nil) +(lem-lsp-base/type:define-request-message workspace/folding-range/refresh + common-lisp:nil + :deprecated + common-lisp:nil + :documentation + "@since 3.18.0 +@proposed" + :error-data + 'common-lisp:nil + :message-direction + "serverToClient" + :method + "workspace/foldingRange/refresh" + :params + 'common-lisp:nil + :partial-result + 'common-lisp:nil + :proposed + common-lisp:t + :registration-method + common-lisp:nil + :registration-options + 'common-lisp:nil + :result + 'lem-lsp-base/type:lsp-null + :since + "3.18.0") + (lem-lsp-base/type:define-request-message text-document/declaration common-lisp:nil :deprecated common-lisp:nil :documentation "A request to resolve the type definition locations of a symbol at a given text -document position. The request's parameter is of type [TextDocumentPositionParams] -(#TextDocumentPositionParams) the response is of type {@link Declaration} -or a typed array of {@link DeclarationLink} or a Thenable that resolves -to such." +document position. The request's parameter is of type {@link TextDocumentPositionParams} +the response is of type {@link Declaration} or a typed array of {@link DeclarationLink} +or a Thenable that resolves to such." :error-data 'common-lisp:nil :message-direction @@ -7564,8 +7665,7 @@ that resolves to such." :registration-options 'selection-range-registration-options :result - '(common-lisp:or (lem-lsp-base/type:lsp-array selection-range) - lem-lsp-base/type:lsp-null) + '(common-lisp:or (lem-lsp-base/type:lsp-array selection-range) lem-lsp-base/type:lsp-null) :since common-lisp:nil) @@ -7623,8 +7723,7 @@ Can be used as an input to an incoming or outgoing call hierarchy. :registration-options 'call-hierarchy-registration-options :result - '(common-lisp:or (lem-lsp-base/type:lsp-array call-hierarchy-item) - lem-lsp-base/type:lsp-null) + '(common-lisp:or (lem-lsp-base/type:lsp-array call-hierarchy-item) lem-lsp-base/type:lsp-null) :since "3.16.0") @@ -7738,8 +7837,7 @@ Can be used as an input to an incoming or outgoing call hierarchy. :registration-options 'semantic-tokens-registration-options :result - '(common-lisp:or semantic-tokens semantic-tokens-delta - lem-lsp-base/type:lsp-null) + '(common-lisp:or semantic-tokens semantic-tokens-delta lem-lsp-base/type:lsp-null) :since "3.16.0") @@ -7866,6 +7964,10 @@ will very likely open the URI in a WEB browser. "The will create files request is sent from the client to the server before files are actually created as long as the creation is triggered from within the client. +The request can return a `WorkspaceEdit` which will be applied to workspace before the +files are created. Hence the `WorkspaceEdit` can not manipulate the content of the file +to be created. + @since 3.16.0" :error-data 'common-lisp:nil @@ -7973,8 +8075,7 @@ The response is of type {@link Moniker Moniker[]} or `null`." :registration-options 'moniker-registration-options :result - '(common-lisp:or (lem-lsp-base/type:lsp-array moniker) - lem-lsp-base/type:lsp-null) + '(common-lisp:or (lem-lsp-base/type:lsp-array moniker) lem-lsp-base/type:lsp-null) :since common-lisp:nil) @@ -8004,8 +8105,7 @@ Can be used as an input to a subtypes or supertypes type hierarchy. :registration-options 'type-hierarchy-registration-options :result - '(common-lisp:or (lem-lsp-base/type:lsp-array type-hierarchy-item) - lem-lsp-base/type:lsp-null) + '(common-lisp:or (lem-lsp-base/type:lsp-array type-hierarchy-item) lem-lsp-base/type:lsp-null) :since "3.17.0") @@ -8034,8 +8134,7 @@ Can be used as an input to a subtypes or supertypes type hierarchy. :registration-options 'common-lisp:nil :result - '(common-lisp:or (lem-lsp-base/type:lsp-array type-hierarchy-item) - lem-lsp-base/type:lsp-null) + '(common-lisp:or (lem-lsp-base/type:lsp-array type-hierarchy-item) lem-lsp-base/type:lsp-null) :since "3.17.0") @@ -8064,8 +8163,7 @@ Can be used as an input to a subtypes or supertypes type hierarchy. :registration-options 'common-lisp:nil :result - '(common-lisp:or (lem-lsp-base/type:lsp-array type-hierarchy-item) - lem-lsp-base/type:lsp-null) + '(common-lisp:or (lem-lsp-base/type:lsp-array type-hierarchy-item) lem-lsp-base/type:lsp-null) :since "3.17.0") @@ -8096,8 +8194,7 @@ type {@link InlineValueParams}, the response is of type :registration-options 'inline-value-registration-options :result - '(common-lisp:or (lem-lsp-base/type:lsp-array inline-value) - lem-lsp-base/type:lsp-null) + '(common-lisp:or (lem-lsp-base/type:lsp-array inline-value) lem-lsp-base/type:lsp-null) :since "3.17.0") @@ -8155,8 +8252,7 @@ type {@link InlayHintsParams}, the response is of type :registration-options 'inlay-hint-registration-options :result - '(common-lisp:or (lem-lsp-base/type:lsp-array inlay-hint) - lem-lsp-base/type:lsp-null) + '(common-lisp:or (lem-lsp-base/type:lsp-array inlay-hint) lem-lsp-base/type:lsp-null) :since "3.17.0") @@ -8305,6 +8401,39 @@ of type {@link InlayHint} or a Thenable that resolves to such. :since "3.17.0") +(lem-lsp-base/type:define-request-message text-document/inline-completion + common-lisp:nil + :deprecated + common-lisp:nil + :documentation + "A request to provide inline completions in a document. The request's parameter is of +type {@link InlineCompletionParams}, the response is of type +{@link InlineCompletion InlineCompletion[]} or a Thenable that resolves to such. + +@since 3.18.0 +@proposed" + :error-data + 'common-lisp:nil + :message-direction + "clientToServer" + :method + "textDocument/inlineCompletion" + :params + 'inline-completion-params + :partial-result + '(lem-lsp-base/type:lsp-array inline-completion-item) + :proposed + common-lisp:t + :registration-method + common-lisp:nil + :registration-options + 'inline-completion-registration-options + :result + '(common-lisp:or inline-completion-list (lem-lsp-base/type:lsp-array inline-completion-item) + lem-lsp-base/type:lsp-null) + :since + "3.18.0") + (lem-lsp-base/type:define-request-message client/register-capability common-lisp:nil :deprecated @@ -8478,8 +8607,7 @@ reliable." :registration-options 'text-document-registration-options :result - '(common-lisp:or (lem-lsp-base/type:lsp-array text-edit) - lem-lsp-base/type:lsp-null) + '(common-lisp:or (lem-lsp-base/type:lsp-array text-edit) lem-lsp-base/type:lsp-null) :since common-lisp:nil) @@ -8514,8 +8642,8 @@ request. However, properties that are needed for the initial sorting and filteri :registration-options 'completion-registration-options :result - '(common-lisp:or (lem-lsp-base/type:lsp-array completion-item) - completion-list lem-lsp-base/type:lsp-null) + '(common-lisp:or (lem-lsp-base/type:lsp-array completion-item) completion-list + lem-lsp-base/type:lsp-null) :since common-lisp:nil) @@ -8610,10 +8738,9 @@ type {@link Hover} or a Thenable that resolves to such." common-lisp:nil :documentation "A request to resolve the definition location of a symbol at a given text -document position. The request's parameter is of type [TextDocumentPosition] -(#TextDocumentPosition) the response is of either type {@link Definition} -or a typed array of {@link DefinitionLink} or a Thenable that resolves -to such." +document position. The request's parameter is of type {@link TextDocumentPosition} +the response is of either type {@link Definition} or a typed array of +{@link DefinitionLink} or a Thenable that resolves to such." :error-data 'common-lisp:nil :message-direction @@ -8663,8 +8790,7 @@ type {@link ReferenceParams} the response is of type :registration-options 'reference-registration-options :result - '(common-lisp:or (lem-lsp-base/type:lsp-array location) - lem-lsp-base/type:lsp-null) + '(common-lisp:or (lem-lsp-base/type:lsp-array location) lem-lsp-base/type:lsp-null) :since common-lisp:nil) @@ -8674,9 +8800,9 @@ type {@link ReferenceParams} the response is of type common-lisp:nil :documentation "Request to resolve a {@link DocumentHighlight} for a given -text document position. The request's parameter is of type [TextDocumentPosition] -(#TextDocumentPosition) the request response is of type [DocumentHighlight[]] -(#DocumentHighlight) or a Thenable that resolves to such." +text document position. The request's parameter is of type {@link TextDocumentPosition} +the request response is an array of type {@link DocumentHighlight} +or a Thenable that resolves to such." :error-data 'common-lisp:nil :message-direction @@ -8694,8 +8820,7 @@ text document position. The request's parameter is of type [TextDocumentPosition :registration-options 'document-highlight-registration-options :result - '(common-lisp:or (lem-lsp-base/type:lsp-array document-highlight) - lem-lsp-base/type:lsp-null) + '(common-lisp:or (lem-lsp-base/type:lsp-array document-highlight) lem-lsp-base/type:lsp-null) :since common-lisp:nil) @@ -8727,8 +8852,7 @@ that resolves to such." 'document-symbol-registration-options :result '(common-lisp:or (lem-lsp-base/type:lsp-array symbol-information) - (lem-lsp-base/type:lsp-array document-symbol) - lem-lsp-base/type:lsp-null) + (lem-lsp-base/type:lsp-array document-symbol) lem-lsp-base/type:lsp-null) :since common-lisp:nil) @@ -8755,9 +8879,8 @@ that resolves to such." :registration-options 'code-action-registration-options :result - '(common-lisp:or - (lem-lsp-base/type:lsp-array (common-lisp:or command code-action)) - lem-lsp-base/type:lsp-null) + '(common-lisp:or (lem-lsp-base/type:lsp-array (common-lisp:or command code-action)) + lem-lsp-base/type:lsp-null) :since common-lisp:nil) @@ -8823,8 +8946,7 @@ resolves to such. 'workspace-symbol-registration-options :result '(common-lisp:or (lem-lsp-base/type:lsp-array symbol-information) - (lem-lsp-base/type:lsp-array workspace-symbol) - lem-lsp-base/type:lsp-null) + (lem-lsp-base/type:lsp-array workspace-symbol) lem-lsp-base/type:lsp-null) :since "3.17.0 - support for WorkspaceSymbol in the returned data. Clients need to advertise support for WorkspaceSymbols via the client capability @@ -8883,8 +9005,7 @@ symbol's location. :registration-options 'code-lens-registration-options :result - '(common-lisp:or (lem-lsp-base/type:lsp-array code-lens) - lem-lsp-base/type:lsp-null) + '(common-lisp:or (lem-lsp-base/type:lsp-array code-lens) lem-lsp-base/type:lsp-null) :since common-lisp:nil) @@ -8967,8 +9088,7 @@ symbol's location. :registration-options 'document-link-registration-options :result - '(common-lisp:or (lem-lsp-base/type:lsp-array document-link) - lem-lsp-base/type:lsp-null) + '(common-lisp:or (lem-lsp-base/type:lsp-array document-link) lem-lsp-base/type:lsp-null) :since common-lisp:nil) @@ -9006,7 +9126,7 @@ is of type {@link DocumentLink} or a Thenable that resolves to such." :deprecated common-lisp:nil :documentation - "A request to to format a whole document." + "A request to format a whole document." :error-data 'common-lisp:nil :message-direction @@ -9024,8 +9144,7 @@ is of type {@link DocumentLink} or a Thenable that resolves to such." :registration-options 'document-formatting-registration-options :result - '(common-lisp:or (lem-lsp-base/type:lsp-array text-edit) - lem-lsp-base/type:lsp-null) + '(common-lisp:or (lem-lsp-base/type:lsp-array text-edit) lem-lsp-base/type:lsp-null) :since common-lisp:nil) @@ -9034,7 +9153,7 @@ is of type {@link DocumentLink} or a Thenable that resolves to such." :deprecated common-lisp:nil :documentation - "A request to to format a range in a document." + "A request to format a range in a document." :error-data 'common-lisp:nil :message-direction @@ -9052,11 +9171,40 @@ is of type {@link DocumentLink} or a Thenable that resolves to such." :registration-options 'document-range-formatting-registration-options :result - '(common-lisp:or (lem-lsp-base/type:lsp-array text-edit) - lem-lsp-base/type:lsp-null) + '(common-lisp:or (lem-lsp-base/type:lsp-array text-edit) lem-lsp-base/type:lsp-null) :since common-lisp:nil) +(lem-lsp-base/type:define-request-message text-document/ranges-formatting + common-lisp:nil + :deprecated + common-lisp:nil + :documentation + "A request to format ranges in a document. + +@since 3.18.0 +@proposed" + :error-data + 'common-lisp:nil + :message-direction + "clientToServer" + :method + "textDocument/rangesFormatting" + :params + 'document-ranges-formatting-params + :partial-result + 'common-lisp:nil + :proposed + common-lisp:t + :registration-method + common-lisp:nil + :registration-options + 'document-range-formatting-registration-options + :result + '(common-lisp:or (lem-lsp-base/type:lsp-array text-edit) lem-lsp-base/type:lsp-null) + :since + "3.18.0") + (lem-lsp-base/type:define-request-message text-document/on-type-formatting common-lisp:nil :deprecated @@ -9080,8 +9228,7 @@ is of type {@link DocumentLink} or a Thenable that resolves to such." :registration-options 'document-on-type-formatting-registration-options :result - '(common-lisp:or (lem-lsp-base/type:lsp-array text-edit) - lem-lsp-base/type:lsp-null) + '(common-lisp:or (lem-lsp-base/type:lsp-array text-edit) lem-lsp-base/type:lsp-null) :since common-lisp:nil) diff --git a/lib/lsp-base/protocol-generator.lisp b/extensions/lsp-base/protocol-generator.lisp similarity index 99% rename from lib/lsp-base/protocol-generator.lisp rename to extensions/lsp-base/protocol-generator.lisp index 00ff79092..a96c39179 100644 --- a/lib/lsp-base/protocol-generator.lisp +++ b/extensions/lsp-base/protocol-generator.lisp @@ -167,7 +167,9 @@ (eswitch (kind :test #'string=) ("base" (assert (member name '("URI" "DocumentUri" "string" "integer") :test #'string=)) - (symbolize name)) + (if (equal name "DocumentUri") + 'lsp-document-uri + (symbolize name))) ("reference" (symbolize name)))) (parse-reference-type hash))) diff --git a/lib/lsp-base/type.lisp b/extensions/lsp-base/type.lisp similarity index 97% rename from lib/lsp-base/type.lisp rename to extensions/lsp-base/type.lisp index 14ce3674b..e2417ece3 100644 --- a/lib/lsp-base/type.lisp +++ b/extensions/lsp-base/type.lisp @@ -194,6 +194,7 @@ (check-initargs instance) instance)) +#+(or) (defmacro define-enum (name (&rest fields) &body options) (declare (ignore options)) (alexandria:with-unique-names (f x anon-name) @@ -209,6 +210,16 @@ :collect `(defparameter ,variable ,value)) ',name)))) +(defmacro define-enum (name (&rest fields) &body options) + (declare (ignore options)) + `(progn + (deftype ,name () + t) + ,@(loop :for (field-name value) :in fields + :for variable := (intern (format nil "~A-~A" name field-name)) + :collect `(defparameter ,variable ,value)) + ',name)) + (defmacro define-type-alias (name type &body options) (let ((doc (second (assoc :documentation options)))) `(deftype ,name () ,@(when `(,doc)) ',type))) diff --git a/lib/lsp-base/utils.lisp b/extensions/lsp-base/utils.lisp similarity index 100% rename from lib/lsp-base/utils.lisp rename to extensions/lsp-base/utils.lisp diff --git a/lib/lsp-base/yason-utils.lisp b/extensions/lsp-base/yason-utils.lisp similarity index 59% rename from lib/lsp-base/yason-utils.lisp rename to extensions/lsp-base/yason-utils.lisp index 60ca054c1..49ac969ef 100644 --- a/lib/lsp-base/yason-utils.lisp +++ b/extensions/lsp-base/yason-utils.lisp @@ -1,31 +1,26 @@ (defpackage :lem-lsp-base/yason-utils (:use :cl) - (:import-from :bordeaux-threads - :*default-special-bindings*) (:export :with-yason-bindings - :update-jsonrpc-yason-parameters :parse-json)) (in-package :lem-lsp-base/yason-utils) (defparameter *yason-bindings* '((yason:*parse-json-null-as-keyword* . t) - (yason:*parse-json-arrays-as-vectors* . t))) + (yason:*parse-json-arrays-as-vectors* . t) + (jsonrpc/yason:*parse-json-null-as-keyword* . t) + (jsonrpc/yason:*parse-json-arrays-as-vectors* . t))) (defmacro with-yason-bindings (() &body body) `(call-with-yason-bindings (lambda () ,@body))) (defun call-with-yason-bindings (function) - (let ((*default-special-bindings* + (let ((bt2:*default-special-bindings* (append *yason-bindings* - *default-special-bindings*))) + bt2:*default-special-bindings*))) (progv (mapcar #'car *yason-bindings*) (mapcar #'cdr *yason-bindings*) (funcall function)))) -(defun update-jsonrpc-yason-parameters () - (setf jsonrpc/yason:*parse-json-null-as-keyword* t - jsonrpc/yason:*parse-json-arrays-as-vectors* t)) - (defun parse-json (input) (with-yason-bindings () (yason:parse input))) diff --git a/extensions/lsp-mode/async-process-stream.lisp b/extensions/lsp-mode/async-process-stream.lisp index 140d61903..aa73be46f 100644 --- a/extensions/lsp-mode/async-process-stream.lisp +++ b/extensions/lsp-mode/async-process-stream.lisp @@ -10,16 +10,21 @@ (buffer-string :initform "" :accessor input-stream-buffer-string) (position :initform 0 - :accessor input-stream-position))) + :accessor input-stream-position) + (logger :initform nil + :initarg :logger + :reader input-stream-logger))) -(defun make-input-stream (process) - (make-instance 'input-stream :process process)) +(defun make-input-stream (process &key logger) + (make-instance 'input-stream :process process :logger logger)) (defun receive-output-if-necessary (stream) (when (<= (length (input-stream-buffer-string stream)) (input-stream-position stream)) - (setf (input-stream-buffer-string stream) - (async-process:process-receive-output (input-stream-process stream))) + (let ((output (async-process:process-receive-output (input-stream-process stream)))) + (setf (input-stream-buffer-string stream) output) + (when (input-stream-logger stream) + (funcall (input-stream-logger stream) output))) (setf (input-stream-position stream) 0))) @@ -48,6 +53,7 @@ (defmethod stream-listen ((stream input-stream)) t) +#+(or) (defmethod stream-read-line ((stream input-stream)) (receive-output-if-necessary stream) (let ((pos (position #\newline diff --git a/extensions/lsp-mode/client-capabilities.json b/extensions/lsp-mode/client-capabilities.json index c4d05c9d1..e6cb53f62 100644 --- a/extensions/lsp-mode/client-capabilities.json +++ b/extensions/lsp-mode/client-capabilities.json @@ -1,57 +1,67 @@ { - "workspace":{ - }, - "textDocument":{ - "synchronization":{ + "workspace": {}, + "textDocument": { + "synchronization": { "didSave": true }, "publishDiagnostics": { "relatedInformation": true }, - "completion":{ - "completionItem":{ + "completion": { + "completionItem": { "snippetSupport": false }, - "contextSupport":true - }, - "hover":{ + "contextSupport": true }, - "signatureHelp":{ - "signatureInformation":{ - "documentationFormat":[ + "hover": {}, + "signatureHelp": { + "signatureInformation": { + "documentationFormat": [ "plaintext", "markdown" ], - "parameterInformation":{ - "labelOffsetSupport":true + "parameterInformation": { + "labelOffsetSupport": true }, "activeParameterSupport": true }, "contextSupport": true }, - "definition":{ - "linkSupport":false - }, - "typeDefinition":{ - "linkSupport":false - }, - "implementation":{ - "linkSupport":false + "definition": { + "linkSupport": false }, - "references":{ + "typeDefinition": { + "linkSupport": false }, - "documentSymbol":{ - "hierarchicalDocumentSymbolSupport":true + "implementation": { + "linkSupport": false }, - "codeAction":{ + "references": {}, + "documentSymbol": { + "hierarchicalDocumentSymbolSupport": true }, - "formatting":{ - }, - "rangeFormatting":{ - }, - "onTypeFormatting":{ + "codeAction": { + "isPreferredSupport": true, + "disabledSupport": true, + "codeActionLiteralSupport": { + "codeActionKind": { + "valueSet": [ + "", + "quickfix", + "refactor", + "refactor.extract", + "refactor.inline", + "refactor.rewrite", + "source", + "source.organizeImports" + ] + } + }, + "dynamicRegistration": false }, - "rename":{ - } + "formatting": {}, + "rangeFormatting": {}, + "onTypeFormatting": {}, + "rename": {} } } diff --git a/extensions/lsp-mode/lem-lsp-mode.asd b/extensions/lsp-mode/lem-lsp-mode.asd index 79507b3ff..4d28191aa 100644 --- a/extensions/lsp-mode/lem-lsp-mode.asd +++ b/extensions/lsp-mode/lem-lsp-mode.asd @@ -1,6 +1,5 @@ (defsystem "lem-lsp-mode" - :depends-on ("lem-socket-utils" - "alexandria" + :depends-on ("alexandria" "cl-package-locks" "jsonrpc" "jsonrpc/transport/stdio" diff --git a/extensions/lsp-mode/lem-stdio-transport.lisp b/extensions/lsp-mode/lem-stdio-transport.lisp index 0797d086e..9bf9095f9 100644 --- a/extensions/lsp-mode/lem-stdio-transport.lisp +++ b/extensions/lsp-mode/lem-stdio-transport.lisp @@ -12,7 +12,18 @@ (defclass lem-stdio-transport (transport) ((process :initarg :process - :reader lem-stdio-transport-process))) + :reader lem-stdio-transport-process) + (stream :initarg :stream + :initform nil + :accessor lem-stdio-transport-stream))) + +(defmethod initialize-instance ((instance lem-stdio-transport) &rest initargs) + (declare (ignore initargs)) + (let ((instance (call-next-method))) + (unless (lem-stdio-transport-stream instance) + (setf (lem-stdio-transport-stream instance) + (make-input-stream (lem-stdio-transport-process instance)))) + instance)) (defmethod start-client ((transport lem-stdio-transport)) (let ((connection (make-instance 'connection @@ -20,11 +31,11 @@ (setf (transport-connection transport) connection) (setf (transport-threads transport) (list - (bt:make-thread + (bt2:make-thread (lambda () (run-processing-loop transport connection)) :name "lem-lsp-mode/lem-stdio-transport processing") - (bt:make-thread + (bt2:make-thread (lambda () (run-reading-loop transport connection)) :name "lem-lsp-mode/lem-stdio-transport reading"))) @@ -32,33 +43,26 @@ (defmethod send-message-using-transport ((transport lem-stdio-transport) connection message) (let* ((json (with-output-to-string (s) - (yason:encode message s)))) + (yason:encode message s))) + (body (format nil + "Content-Length: ~A~C~C~:*~:*~C~C~A" + (babel:string-size-in-octets json) + #\Return + #\Newline + json))) (async-process:process-send-input (lem-stdio-transport-process transport) - (format nil - "Content-Length: ~A~C~C~:*~:*~C~C~A" - (babel:string-size-in-octets json) - #\Return - #\Newline - json)))) + body))) (defmethod receive-message-using-transport ((transport lem-stdio-transport) connection) - (let ((stream (make-input-stream (lem-stdio-transport-process transport)))) - (let* ((headers (handler-case (read-headers stream) - (error () - ;; プロセスを終了したときにread-headersでエラーが出るのでここでハンドリングする - (return-from receive-message-using-transport nil)))) - (length (ignore-errors (parse-integer (gethash "content-length" headers))))) - (when length - (let ((body - (with-output-to-string (out) - (loop - :for c := (read-char stream) - :do (write-char c out) - (decf length (babel:string-size-in-octets (string c))) - (when (<= length 0) - (return)))))) - (jsonrpc:parse-message body)))))) + (let* ((stream (lem-stdio-transport-stream transport)) + (headers (handler-case (read-headers stream) + (error () + ;; プロセスを終了したときにread-headersでエラーが出るのでここでハンドリングする + (return-from receive-message-using-transport nil)))) + (length (ignore-errors (parse-integer (gethash "content-length" headers))))) + (when length + (jsonrpc:parse-message stream)))) (defun read-headers (stream) ;; copied from jsonrpc/transport/stdio::read-headers diff --git a/extensions/lsp-mode/lsp-mode.lisp b/extensions/lsp-mode/lsp-mode.lisp index a9f49491e..dae765142 100644 --- a/extensions/lsp-mode/lsp-mode.lisp +++ b/extensions/lsp-mode/lsp-mode.lisp @@ -18,7 +18,8 @@ (:local-nicknames (:context-menu :lem/context-menu)) (:local-nicknames (:spinner :lem/loading-spinner)) (:local-nicknames (:language-mode :lem/language-mode)) - (:export :get-buffer-from-text-document-identifier + (:export :*inhibit-highlight-diagnotics* + :get-buffer-from-text-document-identifier :spec-initialization-options :register-lsp-method :define-language-spec)) @@ -60,7 +61,7 @@ (point (buffer-point buffer))) (buffer-end point) (insert-string point string)))) - (let* ((port (or (spec-port spec) (lem-socket-utils:random-available-port))) + (let* ((port (or (spec-port spec) (lem/common/socket:random-available-port))) (process (when-let (command (get-spec-command spec port)) (check-exist-program (first command) spec) (lem-process:run-process command :output-callback #'output-callback)))) @@ -360,8 +361,7 @@ (funcall continuation workspace)))) (defun connect (client continuation) - (lem-lsp-base/yason-utils:update-jsonrpc-yason-parameters) - (bt:make-thread + (bt2:make-thread (lambda () (loop :with condition := nil :repeat 20 @@ -488,13 +488,16 @@ (defmethod apply-document-change ((document-change lsp:delete-file)) (error "deleteFile is not yet supported")) +(defun apply-change (uri text-edits) + (let ((buffer (find-buffer-from-uri uri))) + (apply-text-edits buffer text-edits))) + (defun apply-workspace-edit (workspace-edit) (labels ((apply-document-changes (document-changes) (do-sequence (document-change document-changes) (apply-document-change document-change))) (apply-changes (changes) - (declare (ignore changes)) - (error "Not yet implemented"))) + (maphash #'apply-change changes))) (if-let ((document-changes (handler-case (lsp:workspace-edit-document-changes workspace-edit) (unbound-slot () nil)))) @@ -726,7 +729,8 @@ (unbound-slot () 'diagnostic-error-attribute) (:no-error (severity) - (diagnostic-severity-attribute severity)))))) + (diagnostic-severity-attribute severity))) + :end-point-kind :right-inserting))) (overlay-put overlay 'diagnostic (make-diagnostic :buffer buffer @@ -756,7 +760,8 @@ (defun text-document/publish-diagnostics (params) (request::do-request-log "textDocument/publishDiagnostics" params :from :server) (let ((params (convert-from-json params 'lsp:publish-diagnostics-params))) - (send-event (lambda () (highlight-diagnostics params))))) + (send-event (lambda () + (highlight-diagnostics params))))) (define-command lsp-document-diagnostics () () (when-let ((diagnostics (buffer-diagnostics (current-buffer)))) @@ -1773,7 +1778,8 @@ ;;; (define-command lsp-restart-server () () - (dispose-workspace (buffer-workspace (current-buffer))) + (when-let (workspace (buffer-workspace (current-buffer) nil)) + (dispose-workspace workspace)) ;; TODO: ;; 現在のバッファを開き直すだけでは不十分 ;; buffer-listを全て見る必要がある diff --git a/extensions/lua-mode/lem-lua-mode.asd b/extensions/lua-mode/lem-lua-mode.asd index 566f432b8..6bee80bae 100644 --- a/extensions/lua-mode/lem-lua-mode.asd +++ b/extensions/lua-mode/lem-lua-mode.asd @@ -1,5 +1,5 @@ (defsystem "lem-lua-mode" - :depends-on ("lem" "yason") + :depends-on ("lem" "yason" "lem-lsp-mode") :serial t :components ((:file "lua-mode") (:file "lsp-config"))) diff --git a/extensions/lua-mode/lsp-config.lisp b/extensions/lua-mode/lsp-config.lisp index 9bcd43aaf..8fc1ee6bf 100644 --- a/extensions/lua-mode/lsp-config.lisp +++ b/extensions/lua-mode/lsp-config.lisp @@ -1,6 +1,5 @@ (defpackage :lem-lua-mode/lsp-config (:use :cl :lem-lsp-mode :lem-lsp-base/type)) - (in-package :lem-lua-mode/lsp-config) (define-language-spec (lua-spec lem-lua-mode:lua-mode) diff --git a/extensions/markdown-mode/example.md b/extensions/markdown-mode/example.md index 2ef36d35e..d9eca64d5 100644 --- a/extensions/markdown-mode/example.md +++ b/extensions/markdown-mode/example.md @@ -1,5 +1,54 @@ # Hello +## Test + +### Subheader + +#### Deeper header + +##### Even deeper + +###### Deepest header + +This is a paragraph with **bold text**, *italic text*, and __underlined text__. + +Here is some `inline code` and a [link](https://example.com). + +> This is a blockquote. +> More blockquote text. +> > Nested blockquote text. + +Lists: + +- foo +- bar +- baz + +* hoge +* piyo + ++ hoge ++ piyo + +1. one +2. two +3. three + +Task list example: + +- [ ] Incomplete task +- [x] Completed task +- [ ] Another task to do + +Table: + +| Header 1 | Header 2 | Header 3 | +|----------|----------|----------| +| Row 1, Col 1 | Row 1, Col 2 | Row 1, Col 3 | +| Row 2, Col 1 | Row 2, Col 2 | Row 2, Col 3 | + +Code: + ```lisp (defpackage :foo (:use :cl)) @@ -17,20 +66,22 @@ int main(void) { } ``` -## Test +```python +def greet(name): + print(f"Hello, {name}!") -this is test +greet("World") +``` -- foo -- bar -- baz +Inline formatting combinations: +- **Bold and *italic* text** +- *Italic and `inline code`* +- [A link with **bold** text](https://example.com) -* hoge -* piyo +--- -+ hoge -+ piyo +Metadata: -1. one -2. two -3. three +Title: Expanded Comprehensive Markdown Test File +Author: AI Assistant +Date: 2024-08-05 \ No newline at end of file diff --git a/extensions/markdown-mode/internal.lisp b/extensions/markdown-mode/internal.lisp index 93ba354cf..c99f3445f 100644 --- a/extensions/markdown-mode/internal.lisp +++ b/extensions/markdown-mode/internal.lisp @@ -1,8 +1,30 @@ (defpackage :lem-markdown-mode/internal (:use :cl) (:export :on-save - :on-kill)) + :on-kill + :on-change + :preview + :on-save-default + :on-kill-default + :on-change-default + :preview-default)) (in-package :lem-markdown-mode/internal) -(defgeneric on-save (buffer)) -(defgeneric on-kill (buffer)) +(defparameter *view-type* :external-browser) + +(defgeneric on-save (buffer view-type)) +(defgeneric on-kill (buffer view-type)) +(defgeneric on-change (buffer view-type)) +(defgeneric preview (buffer view-type)) + +(defun on-save-default (buffer) + (on-save buffer *view-type*)) + +(defun on-kill-default (buffer) + (on-kill buffer *view-type*)) + +(defun on-change-default (buffer) + (on-change buffer *view-type*)) + +(defun preview-default (buffer) + (preview buffer *view-type*)) diff --git a/extensions/markdown-mode/languages.lisp b/extensions/markdown-mode/languages.lisp index 53be380cc..8f1895c7b 100644 --- a/extensions/markdown-mode/languages.lisp +++ b/extensions/markdown-mode/languages.lisp @@ -7,7 +7,8 @@ `(("common-lisp" . lem-lisp-mode:lisp-mode) ("lisp" . lem-lisp-mode:lisp-mode) ("emacs-lisp" . lem-elisp-mode:elisp-mode) - ("shell" . lem-posix-shell-mode:posix-shell-mode))) + ("shell" . lem-posix-shell-mode:posix-shell-mode) + ("json" . lem-json-mode:json-mode))) (defun find-mode-by-language-name (language-name) (or (cdr (assoc language-name *language-mode-pairs* :test #'equal)) diff --git a/extensions/markdown-mode/lem-markdown-mode.asd b/extensions/markdown-mode/lem-markdown-mode.asd index 9a47b2437..15044df8d 100644 --- a/extensions/markdown-mode/lem-markdown-mode.asd +++ b/extensions/markdown-mode/lem-markdown-mode.asd @@ -4,10 +4,18 @@ "3bmd-ext-code-blocks" "lisp-preprocessor" "trivial-ws" - "trivial-open-browser") + "trivial-open-browser" + "lem-lisp-mode" + "lem-elisp-mode" + "lem-posix-shell-mode" + "lem-json-mode") :serial t :components ((:file "internal") (:file "languages") (:file "syntax-parser") (:file "markdown-mode") - (:file "preview"))) + (:module "preview" + :serial t + :components ((:file "preview") + (:file "external-browser") + (:file "html-buffer"))))) diff --git a/extensions/markdown-mode/markdown-mode.lisp b/extensions/markdown-mode/markdown-mode.lisp index c8e972f86..a88448338 100644 --- a/extensions/markdown-mode/markdown-mode.lisp +++ b/extensions/markdown-mode/markdown-mode.lisp @@ -23,9 +23,13 @@ (variable-value 'tab-width) 4 (variable-value 'calc-indent-function) 'markdown-calc-indent) (add-hook (variable-value 'after-save-hook :buffer (current-buffer)) - 'lem-markdown-mode/internal:on-save) + 'lem-markdown-mode/internal:on-save-default) (add-hook (variable-value 'kill-buffer-hook :buffer (current-buffer)) - 'lem-markdown-mode/internal:on-kill)) + 'lem-markdown-mode/internal:on-kill-default) + (add-hook (variable-value 'after-change-functions :buffer (current-buffer)) + (lambda (start end old-len) + (declare (ignore end old-len)) + (lem-markdown-mode/internal:on-change-default (point-buffer start))))) (define-key *markdown-mode-keymap* "C-c C-l" 'markdown-insert-link) diff --git a/extensions/markdown-mode/index.html b/extensions/markdown-mode/preview/external-browser-preview.html similarity index 100% rename from extensions/markdown-mode/index.html rename to extensions/markdown-mode/preview/external-browser-preview.html diff --git a/extensions/markdown-mode/preview.lisp b/extensions/markdown-mode/preview/external-browser.lisp similarity index 79% rename from extensions/markdown-mode/preview.lisp rename to extensions/markdown-mode/preview/external-browser.lisp index f39d7a38c..efe705979 100644 --- a/extensions/markdown-mode/preview.lisp +++ b/extensions/markdown-mode/preview/external-browser.lisp @@ -1,13 +1,15 @@ -(defpackage :lem-markdown-mode/preview - (:use #:cl - #:lem) +(defpackage :lem-markdown-mode/preview/external-browser + (:use :cl + :lem) + (:import-from :lem-markdown-mode/preview/preview + :render) (:documentation "Live rendering of markdown documents to a browser. This package defines the command M-x markdown-preview that opens a browser window, connects to it via a websocket, and renders and updates the markdown in the browser at each file save. The WS connection is closed when the markdown file is closed.")) -(in-package :lem-markdown-mode/preview) +(in-package :lem-markdown-mode/preview/external-browser) (defclass server (trivial-ws:server) ((handler :initform nil @@ -30,14 +32,9 @@ The WS connection is closed when the markdown file is closed.")) (defvar *template* (lisp-preprocessor:compile-template - (asdf:system-relative-pathname :lem-markdown-mode "index.html") + (asdf:system-relative-pathname :lem-markdown-mode "preview/external-browser-preview.html") :arguments '($websocket-url $body))) -(defun render (string) - (let ((3bmd-code-blocks:*code-blocks* t)) - (with-output-to-string (stream) - (3bmd:parse-string-and-print-to-stream string stream)))) - (defun generate-html (buffer websocket-port) (uiop:with-temporary-file (:stream out :pathname html-file @@ -66,7 +63,7 @@ The WS connection is closed when the markdown file is closed.")) (let ((server (get-buffer-server-or-make buffer))) (when (server-handler server) (trivial-ws:stop (server-handler server))) - (let ((port (lem-socket-utils:random-available-port))) + (let ((port (lem/common/socket:random-available-port))) (setf (server-handler server) (trivial-ws:start server port)) port))) @@ -84,15 +81,18 @@ The WS connection is closed when the markdown file is closed.")) (error (e) (log:error e))))))) -(defmethod lem-markdown-mode/internal:on-save (buffer) +(defmethod lem-markdown-mode/internal:on-save (buffer (view-type (eql :external-browser))) (refresh buffer)) -(defmethod lem-markdown-mode/internal:on-kill (buffer) +(defmethod lem-markdown-mode/internal:on-kill (buffer (view-type (eql :external-browser))) (alexandria:when-let* ((server (buffer-server buffer)) (handler (server-handler server))) (trivial-ws:stop handler))) -(define-command markdown-preview () () +(defmethod lem-markdown-mode/internal:on-change (buffer (view-type (eql :external-browser))) + ) + +(defmethod lem-markdown-mode/internal:preview (buffer (view-type (eql :external-browser))) "Render the markdown of the current buffer to a browser window. The preview is refreshed when the file is saved. The connection is closed when the file is closed." diff --git a/extensions/markdown-mode/preview/html-buffer.lisp b/extensions/markdown-mode/preview/html-buffer.lisp new file mode 100644 index 000000000..2d784ea44 --- /dev/null +++ b/extensions/markdown-mode/preview/html-buffer.lisp @@ -0,0 +1,40 @@ +(uiop:define-package :lem-markdown-mode/preview/html-buffer + (:use :cl :lem) + (:import-from :lem-markdown-mode/preview/preview + :render)) +(in-package :lem-markdown-mode/preview/html-buffer) + +(defun preview-buffer-name (buffer) + (format nil + "*Markdown Preview ~A*" + (buffer-name buffer))) + +(defmethod lem-markdown-mode/internal:preview (buffer (view-type (eql :html-buffer))) + (let* ((html (render (buffer-text buffer))) + (html (format nil " + +~A + " html)) + (html-buffer (lem:make-html-buffer (preview-buffer-name buffer) + html))) + (pop-to-buffer html-buffer))) + +(defmethod lem-markdown-mode/internal:on-save (buffer (view-type (eql :html-buffer))) + (when (get-buffer (preview-buffer-name buffer)) + (lem-markdown-mode/internal:preview buffer :html-buffer))) + +(defmethod lem-markdown-mode/internal:on-kill (buffer (view-type (eql :html-buffer))) + ) + +(defmethod lem-markdown-mode/internal:on-change (buffer (view-type (eql :html-buffer))) + (alexandria:when-let (html-buffer (get-buffer (preview-buffer-name buffer))) + (let ((html (with-output-to-string (out) + (yason:encode (render (buffer-text buffer)) + out))) + (window (pop-to-buffer html-buffer))) + (lem-if:js-eval (lem:implementation) + (window-view window) + (format nil " +const main = document.getElementById(\"main\"); +main.innerHTML = ~A; +" html))))) diff --git a/extensions/markdown-mode/preview/preview.lisp b/extensions/markdown-mode/preview/preview.lisp new file mode 100644 index 000000000..81a72e7cd --- /dev/null +++ b/extensions/markdown-mode/preview/preview.lisp @@ -0,0 +1,11 @@ +(uiop:define-package :lem-markdown-mode/preview/preview + (:use :cl :lem)) +(in-package :lem-markdown-mode/preview/preview) + +(define-command markdown-preview () () + (lem-markdown-mode/internal:preview (current-buffer) :html-buffer)) + +(defun render (string) + (let ((3bmd-code-blocks:*code-blocks* t)) + (with-output-to-string (stream) + (3bmd:parse-string-and-print-to-stream string stream)))) diff --git a/extensions/markdown-mode/syntax-parser.lisp b/extensions/markdown-mode/syntax-parser.lisp index f6f0e1212..af68e4faf 100644 --- a/extensions/markdown-mode/syntax-parser.lisp +++ b/extensions/markdown-mode/syntax-parser.lisp @@ -51,32 +51,89 @@ :syntax-table syntax-table :recursive-check nil)) (t - (put-text-property start point :attribute 'syntax-string-attribute)))))) + (put-text-property start point :attribute 'document-code-block-attribute)))))) (defun scan-region (start end) (clear-region-major-mode start end) (with-point ((point start)) (loop :while (point< point end) - :do (cond ((looking-at point "^#") - (put-line-attribute point 'syntax-constant-attribute)) - ((looking-at point "^>") - (put-line-attribute point 'syntax-string-attribute)) - ((looking-at point "^\\s*[-*+]") - (back-to-indentation point) - (with-point ((start point) - (end point)) - (character-offset end 1) - (put-text-property start end :attribute 'syntax-keyword-attribute))) - ((looking-at point "^\\s*(?:\\d)+\\.\\s") - (back-to-indentation point) - (with-point ((start point) - (end point)) - (skip-chars-forward end #'digit-char-p) - (character-offset end 1) - (put-text-property start end :attribute 'syntax-keyword-attribute))) - ((start-code-block-line-p point) - (scan-code-block point end))) - :while (line-offset point 1)))) + :do (let ((line (line-string point))) + (cond + ((str:starts-with-p "#" line) + (let ((level (position #\Space line))) + (when level + (put-line-attribute point (case level + (1 'document-header1-attribute) + (2 'document-header2-attribute) + (3 'document-header3-attribute) + (4 'document-header4-attribute) + (5 'document-header5-attribute) + (6 'document-header6-attribute)))))) + ((str:starts-with-p ">" line) + (put-line-attribute point 'document-blockquote-attribute)) + ;; Unordered list items + ((ppcre:scan "^\\s*[-*+]\\s" line) + (ppcre:do-matches (start end "^(\\s*[-*+])\\s" line) + (put-text-property + (character-offset (copy-point point) start) + (character-offset (copy-point point) end) + :attribute 'document-list-attribute))) + ;; Ordered list items + ((ppcre:scan "^\\s*\\d+\\.\\s" line) + (ppcre:do-matches (start end "^(\\s*\\d+\\.)\\s" line) + (put-text-property + (character-offset (copy-point point) start) + (character-offset (copy-point point) end) + :attribute 'document-list-attribute))) + ((start-code-block-line-p point) + (scan-code-block point end)) + ((or (str:starts-with-p "- [ ]" line) + (str:starts-with-p "- [x]" line) + (str:starts-with-p "- [X]" line)) + (put-line-attribute point 'document-task-list-attribute)) + ((str:starts-with-p "---" line) + (put-line-attribute point 'document-metadata-attribute)) + ((str:starts-with-p "|" line) + (put-line-attribute point 'document-table-attribute))) + + ;; Inline matches + ;; Bold + (ppcre:do-matches (start end "\\*\\*(.*?)\\*\\*" line) + (put-text-property + (character-offset (copy-point point) start) + (character-offset (copy-point point) end) + :attribute 'document-bold-attribute)) + ;; Italic + (ppcre:do-matches (start end "\\*(.*?)\\*" line) + (let ((bold-start (character-offset (copy-point point) start)) + (bold-end (character-offset (copy-point point) end))) + (unless (text-property-at bold-start :attribute) + (put-text-property + bold-start + bold-end + :attribute 'document-italic-attribute)))) + ;; Underline + (ppcre:do-matches (start end "__(.*?)__" line) + (put-text-property + (character-offset (copy-point point) start) + (character-offset (copy-point point) end) + :attribute 'document-underline-attribute)) + ;; Code + (ppcre:do-matches (start end "`([^`]+)`" line) + (put-text-property + (character-offset (copy-point point) start) + (character-offset (copy-point point) end) + :attribute 'document-inline-code-attribute)) + ;; Links + (ppcre:do-matches (start end "\\[([^\\]]+)\\]\\(([^)]+)\\)" line) + (put-text-property + (character-offset (copy-point point) start) + (character-offset (copy-point point) end) + :attribute 'document-link-attribute))) + ; Exit if we can't move forward + :do (unless (line-offset point 1) + (return))))) + (defun search-backward-code-block-start (point) (with-point ((point point)) diff --git a/extensions/nix-mode/indent.lisp b/extensions/nix-mode/indent.lisp new file mode 100644 index 000000000..7926a47b0 --- /dev/null +++ b/extensions/nix-mode/indent.lisp @@ -0,0 +1,22 @@ +(defpackage :lem-nix-mode/indent + (:use :cl + :lem + :lem/language-mode) + (:export :beginning-of-defun + :end-of-defun + :calc-indent)) +(in-package :lem-nix-mode/indent) + +(defun beginning-of-defun (point n) + (loop :repeat n :do (search-backward-regexp point "^\\{"))) + +(defun end-of-defun (point n) + (if (minusp n) + (beginning-of-defun point (- n)) + (search-forward-regexp point "^\\}"))) + +(defun calc-indent (point) + (with-point ((point point)) + (let ((tab-width 2) + (column (point-column point))) + (+ column (- tab-width (rem column tab-width)))))) diff --git a/extensions/nix-mode/lem-nix-mode.asd b/extensions/nix-mode/lem-nix-mode.asd new file mode 100644 index 000000000..9a6c9b1be --- /dev/null +++ b/extensions/nix-mode/lem-nix-mode.asd @@ -0,0 +1,5 @@ +(defsystem "lem-nix-mode" + :depends-on ("lem") + :serial t + :components ((:file "indent") + (:file "nix-mode"))) diff --git a/extensions/nix-mode/nix-mode.lisp b/extensions/nix-mode/nix-mode.lisp new file mode 100644 index 000000000..0151ceda2 --- /dev/null +++ b/extensions/nix-mode/nix-mode.lisp @@ -0,0 +1,59 @@ +(defpackage :lem-nix-mode + (:use :cl + :lem + :lem/language-mode + :lem/language-mode-tools) + (:local-nicknames (:indent :lem-nix-mode/indent)) + (:export :nix-mode)) +(in-package :lem-nix-mode) + +(defun tokens (boundary strings) + (let ((alternation + `(:alternation ,@(sort (copy-list strings) #'> :key #'length)))) + (if boundary + `(:sequence ,boundary ,alternation ,boundary) + alternation))) + +(defun make-tmlanguage-nix () + (let ((patterns (make-tm-patterns + (make-tm-region '(:sequence "#") "$" :name 'syntax-comment-attribute) + (make-tm-region '(:sequence "''") + '(:sequence "''") + :name 'syntax-string-attribute) + (make-tm-match (tokens :word-boundary + '("true" + "false" + "null")) + :name 'syntax-keyword-attribute) + (make-tm-string-region "''") + (make-tm-string-region "\"")))) + (make-tmlanguage :patterns patterns))) + +(defvar *syntax-table* + (let ((table (make-syntax-table + :space-chars '(#\space #\tab #\newline) + :symbol-chars '(#\_) + :paren-pairs '((#\( . #\)) + (#\{ . #\}) + (#\[ . #\])) + :string-quote-chars '(#\") + :block-string-pairs '(("''" . "''")) + :line-comment-string "#"))) + (set-syntax-parser table (make-tmlanguage-nix)) + table)) + +(define-major-mode nix-mode language-mode + (:name "Nix" + :keymap *nix-mode-keymap* + :syntax-table *syntax-table* + :mode-hook *nix-mode-hook*) + (setf (variable-value 'enable-syntax-highlight) t + (variable-value 'indent-tabs-mode) nil + (variable-value 'tab-width) 8 + (variable-value 'calc-indent-function) 'indent:calc-indent + (variable-value 'line-comment) "#" + (variable-value 'beginning-of-defun-function) 'indent:beginning-of-defun + (variable-value 'end-of-defun-function) 'indent:end-of-defun + )) + +(define-file-type ("nix") nix-mode) diff --git a/extensions/paredit-mode/paredit-mode.lisp b/extensions/paredit-mode/paredit-mode.lisp index 1724a050d..b88bc1de6 100644 --- a/extensions/paredit-mode/paredit-mode.lisp +++ b/extensions/paredit-mode/paredit-mode.lisp @@ -11,9 +11,13 @@ link : http://www.daregada.sakuraweb.com/paredit_tutorial_ja.html :paredit-insert-paren :paredit-insert-doublequote :paredit-insert-vertical-line + :paredit-insert-bracket + :paredit-insert-brace :paredit-backward-delete :paredit-forward-delete :paredit-close-parenthesis + :paredit-close-bracket + :paredit-close-brace :paredit-kill :paredit-slurp :paredit-barf @@ -24,7 +28,8 @@ link : http://www.daregada.sakuraweb.com/paredit_tutorial_ja.html :paredit-wrap-round :paredit-meta-doublequote :paredit-vertical-line-wrap - :*paredit-mode-keymap*)) + :*paredit-mode-keymap* + :*remove-whitespace*)) (in-package :lem-paredit-mode) (define-minor-mode paredit-mode @@ -32,6 +37,8 @@ link : http://www.daregada.sakuraweb.com/paredit_tutorial_ja.html :description "Helps to handle parentheses balanced in your Lisp code." :keymap *paredit-mode-keymap*)) +(defvar *remove-whitespace* nil "Aggressively remove whitespace on some actions") + (defun move-to-word-end (q) (loop while (not (syntax-space-char-p (character-at q))) do (character-offset q 1))) @@ -305,16 +312,19 @@ link : http://www.daregada.sakuraweb.com/paredit_tutorial_ja.html ((syntax-escape-point-p p 0) (insert-character p c)) ((char= c (character-at p)) - (if (syntax-escape-point-p p 0) - (insert-character p c) - (forward-char))) + (when *remove-whitespace* + (with-point ((from p)) + (skip-whitespace-backward from) + (delete-between-points from p))) + (character-offset (current-point) 1)) ((ignore-errors (or (scan-lists p 1 1)) t) (with-point ((new-p p)) (character-offset new-p -1) (move-point (current-point) new-p) (with-point ((p new-p)) (skip-whitespace-backward p) - (delete-between-points p new-p)))) + (delete-between-points p new-p) + (character-offset (current-point) 1)))) (t (insert-character p c))))) @@ -367,6 +377,16 @@ link : http://www.daregada.sakuraweb.com/paredit_tutorial_ja.html (return)))) (kill-region origin kill-end))))) +(defun is-inside-empty-parens (point) + (with-point ((left point) + (right point)) + (skip-whitespace-backward left) + (character-offset left -1) + (skip-whitespace-forward right) + (and (syntax-open-paren-char-p (character-at left)) + (syntax-closed-paren-char-p (character-at right)) + (syntax-equal-paren-p left right)))) + (define-command paredit-slurp () () (with-point ((origin (current-point)) (kill-point (current-point))) @@ -384,19 +404,27 @@ link : http://www.daregada.sakuraweb.com/paredit_tutorial_ja.html (move-point (current-point) origin) (indent-points origin yank-point))) (t - (scan-lists kill-point 1 1) - (character-offset kill-point -1) - (%skip-closed-parens-and-whitespaces-forward kill-point nil) - (character-offset kill-point -1) - (with-point ((yank-point kill-point :left-inserting)) - (%skip-closed-parens-and-whitespaces-forward yank-point t) - (unless (end-buffer-p yank-point) - (let ((c (character-at kill-point))) - (form-offset yank-point 1) - (insert-character yank-point c) - (delete-character kill-point)) - (move-point (current-point) origin) - (indent-points origin yank-point))))))) + (let ((remove-whitespace (and *remove-whitespace* (is-inside-empty-parens origin)))) + (scan-lists kill-point 1 1) + (character-offset kill-point -1) + (%skip-closed-parens-and-whitespaces-forward kill-point nil) + (character-offset kill-point -1) + (with-point ((yank-point kill-point :left-inserting)) + (%skip-closed-parens-and-whitespaces-forward yank-point t) + (unless (end-buffer-p yank-point) + (let ((c (character-at kill-point))) + (form-offset yank-point 1) + (insert-character yank-point c) + (delete-character kill-point)) + (when remove-whitespace + (with-point ((from origin) + (to origin)) + (skip-whitespace-backward from) + (skip-whitespace-forward to) + (delete-between-points from to) + (setf origin from))) + (move-point (current-point) origin) + (indent-points origin yank-point)))))))) (define-command paredit-barf () () (with-point ((origin (current-point) :right-inserting) @@ -587,6 +615,7 @@ link : http://www.daregada.sakuraweb.com/paredit_tutorial_ja.html ("C-Left" 'paredit-barf) ("M-s" 'paredit-splice) ("M-Up" 'paredit-splice-backward) + ("M-Down" 'paredit-splice-forward) ("M-r" 'paredit-raise) ("M-(" 'paredit-wrap-round) ("M-|" 'paredit-vertical-line-wrap) diff --git a/lib/process/lem-process.asd b/extensions/process/lem-process.asd similarity index 100% rename from lib/process/lem-process.asd rename to extensions/process/lem-process.asd diff --git a/lib/process/package.lisp b/extensions/process/package.lisp similarity index 100% rename from lib/process/package.lisp rename to extensions/process/package.lisp diff --git a/lib/process/process.lisp b/extensions/process/process.lisp similarity index 94% rename from lib/process/process.lisp rename to extensions/process/process.lisp index 318bdabcf..189bef528 100644 --- a/lib/process/process.lisp +++ b/extensions/process/process.lisp @@ -36,7 +36,7 @@ ;; :read-thread thread :output-callback output-callback :output-callback-type output-callback-type)) - (thread (bt:make-thread + (thread (bt2:make-thread (lambda () (loop (unless (async-process:process-alive-p pointer) @@ -65,8 +65,8 @@ (funcall output-callback string)))))) (defun delete-process (process) - (when (bt:thread-alive-p (process-read-thread process)) - (bt:destroy-thread (process-read-thread process))) + (when (bt2:thread-alive-p (process-read-thread process)) + (bt2:destroy-thread (process-read-thread process))) (async-process:delete-process (process-pointer process))) (defun process-alive-p (process) diff --git a/lib/process/stream.lisp b/extensions/process/stream.lisp similarity index 100% rename from lib/process/stream.lisp rename to extensions/process/stream.lisp diff --git a/extensions/ruby-mode/lem-ruby-mode.asd b/extensions/ruby-mode/lem-ruby-mode.asd new file mode 100644 index 000000000..2ee2c447f --- /dev/null +++ b/extensions/ruby-mode/lem-ruby-mode.asd @@ -0,0 +1,4 @@ +(defsystem "lem-ruby-mode" + :depends-on ("lem" "lem-js-mode") + :serial t + :components ((:file "ruby-mode"))) diff --git a/extensions/ruby-mode/ruby-mode.lisp b/extensions/ruby-mode/ruby-mode.lisp new file mode 100644 index 000000000..974a69aac --- /dev/null +++ b/extensions/ruby-mode/ruby-mode.lisp @@ -0,0 +1,174 @@ +(defpackage :lem-ruby-mode + (:use :cl :lem :lem/language-mode :lem/language-mode-tools) + (:import-from :lem-js-mode + :get-line-indent + :move-to-previous-line) + (:export :ruby-mode)) + +(in-package :lem-ruby-mode) + +(defvar *ruby-keywords* + '("alias" "break" + "defined\?" "fail" + "end" "next" + "redo" "retry" + "return" "self" "super" + "undef" "yield" "private" + "public" "protected")) + +(defvar *ruby-block-beg-keywords* + '("class" "module" "def" "case" "for" "begin" "do")) + +(defvar *ruby-modifier-beg-keywords* + '("if" "unless" "while" "until")) + +(defvar *ruby-block-mid-keywords* + '("then" "else" "elsif" "when" "in" "rescue" "ensure")) + +(defvar *ruby-block-op-keywords* + '("and" "or" "not")) + +(defvar *ruby-block-start* + "(^|\\s)(def|class|module|loop|for|begin|do)\\b") + +(defvar *ruby-multiline-block-start* + "^\\s*(if|then|until|unless|while)\\b") + +(defvar *ruby-block-end* + "(^|\\s)(end)\\b") + +;; Regex to check if a line contains an opening/closing parenthesis/bracket +(defvar *paren-bracket-open-close-pattern* "^.*[\(\[\{].*[\\}\\)\\]].*$") +(defvar *paren-bracket-open-pattern* "^.*[\(\[\{].*$") +(defvar *paren-bracket-close-pattern* "^.*[\\)\\]\\}].*$") + +(defvar *ruby-boolean-literals* + '("true" "false")) + +(defvar *ruby-null-literal* + '("nil")) + +;; From: https://www.rubyguides.com/2018/07/ruby-operators/ +(defvar *ruby-operators* + '("+" "*" "++" "--" "<>" "||" "&&" "!" + "==" "!=" "===" "!==" ">=" "<=" "<=>" + "?" "~" "<<" ">>" "%" "|")) + +(defun tokens (boundary strings) + (let ((alternation + `(:alternation ,@(sort (copy-list strings) #'> :key #'length)))) + (if boundary + `(:sequence ,boundary ,alternation ,boundary) + alternation))) + + +(defun make-tmlanguage-ruby () + (let* ((patterns (make-tm-patterns + (make-tm-match ":\\w+" + :name 'syntax-constant-attribute) + (make-tm-match "\\w+:" + :name 'syntax-constant-attribute) + (make-tm-line-comment-region "#") + (make-tm-string-region "\"") + (make-tm-string-region "'") + (make-tm-string-region "\"\"\"") + (make-tm-match (tokens :word-boundary + (append *ruby-boolean-literals* + *ruby-null-literal*)) + :name 'syntax-constant-attribute) + (make-tm-match (tokens :word-boundary (append + *ruby-keywords* + *ruby-block-op-keywords* + *ruby-block-mid-keywords* + *ruby-modifier-beg-keywords* + *ruby-block-beg-keywords*)) + :name 'syntax-keyword-attribute) + (make-tm-match (tokens :word-boundary *ruby-operators*) + :name 'syntax-builtin-attribute)))) + (make-tmlanguage :patterns patterns))) + +(defvar *ruby-syntax-table* + (let ((table (make-syntax-table + :space-chars '(#\space #\tab #\newline) + :paren-pairs '((#\( . #\)) + (#\{ . #\}) + (#\[ . #\])) + :string-quote-chars '(#\" #\') + :block-string-pairs '(("\"\"\"" . "\"\"\"") + ("'''" . "'''")) + :line-comment-string "#")) + (tmlanguage (make-tmlanguage-ruby))) + (set-syntax-parser table tmlanguage) + table)) + + +(define-major-mode ruby-mode language-mode + (:name "Ruby" + :syntax-table *ruby-syntax-table* + :mode-hook *ruby-mode-hook*) + (setf (variable-value 'enable-syntax-highlight) t + (variable-value 'indent-tabs-mode) nil + (variable-value 'calc-indent-function) 'ruby-calc-indent + (variable-value 'tab-width) 2 + (variable-value 'line-comment) "#" + (variable-value 'beginning-of-defun-function) 'beginning-of-defun + (variable-value 'end-of-defun-function) 'end-of-defun)) + +(defun beginning-of-defun (point n) + (loop :with regex = *ruby-block-start* + :repeat n + :do (search-backward-regexp point regex))) + + +(defun end-of-defun (point n) + (with-point ((p point)) + (loop :repeat n + :do (line-offset p 1) + (unless (search-forward-regexp p "^ end") (return))) + (line-start p) + (move-point point p))) + +(defun open-parens-bracket (start end) + (and (not (search-backward-regexp end *paren-bracket-open-close-pattern* start)) + (search-backward-regexp end *paren-bracket-open-pattern* start))) + + +(defun close-parens-bracket (start end) + (and (not (search-backward-regexp end *paren-bracket-open-close-pattern* start)) + (search-backward-regexp end *paren-bracket-close-pattern* start))) + +(defun ruby-calc-indent (point) + (with-point ((prev point)) + (move-to-previous-line prev) + (with-point ((p point)) + (if (start-buffer-p (line-start p)) + (return-from ruby-calc-indent 0))) + (let ((tab-width (variable-value 'tab-width :default point)) + (column (length (get-line-indent prev)))) + (when (in-string-or-comment-p point) + (with-point ((p point)) + (back-to-indentation p) + (return-from ruby-calc-indent (point-column p)))) + + (with-point ((p point)) + (when (move-to-previous-line p) + (with-point ((start p) + (end p)) + (line-start start) + (line-end end) + (when (or (search-backward-regexp end *ruby-block-start* start) + (search-backward-regexp end *ruby-multiline-block-start* start) + (open-parens-bracket start end)) + (return-from ruby-calc-indent (incf column tab-width)))))) + + (with-point ((p point) + (start point) + (end point)) + (line-start start) + (line-end end) + (if (or (close-parens-bracket start end) + (search-forward-regexp p *ruby-block-end* (line-end end)) ) + (decf column tab-width))) + column))) + +(define-file-type ("rb" "rspec" "Gemfile") ruby-mode) diff --git a/extensions/scheme-mode/lem-scheme-mode.asd b/extensions/scheme-mode/lem-scheme-mode.asd index 4eba067de..53a84c9ad 100644 --- a/extensions/scheme-mode/lem-scheme-mode.asd +++ b/extensions/scheme-mode/lem-scheme-mode.asd @@ -6,8 +6,7 @@ "uiop" "swank" #+#.(cl:if (asdf:find-system :async-process cl:nil) '(and) '(or)) "lem-process" - "lem" - "lem-socket-utils") + "lem") :serial t :components ((:file "syntax-data") (:file "syntax-indent") diff --git a/extensions/scheme-mode/package.lisp b/extensions/scheme-mode/package.lisp index 03e1b90b3..189fd85d0 100644 --- a/extensions/scheme-mode/package.lisp +++ b/extensions/scheme-mode/package.lisp @@ -4,8 +4,7 @@ :lem/completion-mode :lem/language-mode :lem-scheme-mode.errors - :lem-scheme-mode.swank-protocol - :lem-socket-utils) + :lem-scheme-mode.swank-protocol) (:export ;; scheme-mode.lisp :scheme-mode diff --git a/extensions/scheme-mode/swank-connection.lisp b/extensions/scheme-mode/swank-connection.lisp index 47bc62cd4..688ae102a 100644 --- a/extensions/scheme-mode/swank-connection.lisp +++ b/extensions/scheme-mode/swank-connection.lisp @@ -77,6 +77,7 @@ (connection-implementation-version c) (connection-command c))) :select-callback (lambda (menu c) + (declare (ignore menu)) (change-current-connection c))))) (defun check-connection () @@ -120,7 +121,7 @@ (buffer-package (current-buffer)) (connection-package *connection*))) -(defun current-swank-thread () +(defun current-micros-thread () (or (buffer-value (current-buffer) 'thread) t)) @@ -144,7 +145,7 @@ (defun scheme-rex (form &key continuation - (thread (current-swank-thread)) + (thread (current-micros-thread)) (package (current-package))) (emacs-rex *connection* form @@ -154,7 +155,7 @@ (defun scheme-eval-internal (emacs-rex-fun rex-arg package) (let ((tag (gensym)) - (thread-id (current-swank-thread))) + (thread-id (current-micros-thread))) (catch tag (funcall emacs-rex-fun *connection* @@ -196,7 +197,7 @@ (unless *suppress-error-disp* (message "Evaluation aborted on ~A." condition)))) (setf *suppress-error-disp* nil)) - :thread (current-swank-thread) + :thread (current-micros-thread) :package package))) (defun eval-with-transcript (form) @@ -298,7 +299,7 @@ (check-connection) (send-message-string *connection* - (format nil "(:emacs-interrupt ~A)" (current-swank-thread)))) + (format nil "(:emacs-interrupt ~A)" (current-micros-thread)))) (defun prompt-for-sexp (string &optional initial) (prompt-for-string string @@ -737,13 +738,13 @@ (defvar *wait-message-thread* nil) (defun notify-change-connection-to-wait-message-thread () - (bt:interrupt-thread *wait-message-thread* + (bt2:interrupt-thread *wait-message-thread* (lambda () (error 'change-connection)))) (defun start-thread () (unless *wait-message-thread* (setf *wait-message-thread* - (bt:make-thread + (bt2:make-thread (lambda () (loop :named exit :do @@ -975,7 +976,7 @@ (write-line "(loop (sleep most-positive-fixnum))" out))) (defun run-swank-server (command port &key (directory (buffer-directory))) - (bt:make-thread + (bt2:make-thread (lambda () (with-input-from-string (input (initialize-forms-string port)) @@ -1008,8 +1009,8 @@ (defun run-slime (command &key (directory (buffer-directory))) ;;(unless command ;; (setf command (get-lisp-command :impl *impl-name*))) - (let ((port (or (lem-socket-utils:port-available-p *default-port*) - (lem-socket-utils:random-available-port)))) + (let ((port (or (lem/common/socket:port-available-p *default-port*) + (lem/common/socket:random-available-port)))) ;; for r7rs-swank (make command) (unless command @@ -1023,7 +1024,7 @@ (let ((thread (run-swank-server command port :directory directory))) (sleep 0.5) - (unless (bt:thread-alive-p thread) + (unless (bt2:thread-alive-p thread) (editor-error "Scheme swank server start error"))) (let ((successp) diff --git a/extensions/swift-mode/swift-mode.lisp b/extensions/swift-mode/swift-mode.lisp index d9ad5951d..134acd4e8 100644 --- a/extensions/swift-mode/swift-mode.lisp +++ b/extensions/swift-mode/swift-mode.lisp @@ -26,6 +26,7 @@ (define-major-mode swift-mode language-mode (:name "Swift" + :keymap *swfit-mode-keymap* :syntax-table *swift-syntax-table* :mode-hook *swift-mode-hook*) (setf (variable-value 'enable-syntax-highlight) t) @@ -34,8 +35,3 @@ (setf (variable-value 'insertion-line-comment) "// ")) (define-file-type ("swift") swift-mode) - - - - - diff --git a/extensions/terminal/Dockerfile b/extensions/terminal/Dockerfile new file mode 100644 index 000000000..6491247cb --- /dev/null +++ b/extensions/terminal/Dockerfile @@ -0,0 +1,14 @@ +FROM ubuntu:14.04 +RUN apt-get update && apt-get install -y \ + build-essential curl libtool \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* +RUN cd /tmp \ + && curl -O https://www.leonerd.org.uk/code/libvterm/libvterm-0.3.3.tar.gz \ + && tar xf libvterm-0.3.3.tar.gz \ + && cd /tmp/libvterm-0.3.3 \ + && export CXXFLAGS="$CXXFLAGS -fPIC" \ + && export CFLAGS="$CFLAGS -fPIC" make \ + && make install clean \ + && rm -rf /tmp/libvterm-0.3.3 +#docker build . -t libvterm && docker run -v $PWD:/build --rm libvterm /bin/bash -c "cd /build/;gcc terminal.c -Wl,-Bstatic -lvterm -Wl,-Bdynamic -o terminal.so -shared -fPIC -lutil" diff --git a/extensions/terminal/Dockerfile.musl b/extensions/terminal/Dockerfile.musl new file mode 100644 index 000000000..4cdebba07 --- /dev/null +++ b/extensions/terminal/Dockerfile.musl @@ -0,0 +1,11 @@ +FROM alpine:3.17 +RUN apk add --no-cache alpine-sdk curl-dev libtool +RUN cd /tmp \ + && curl -O https://www.leonerd.org.uk/code/libvterm/libvterm-0.3.3.tar.gz \ + && tar xf libvterm-0.3.3.tar.gz \ + && cd /tmp/libvterm-0.3.3 \ + && export CXXFLAGS="$CXXFLAGS -fPIC" \ + && export CFLAGS="$CFLAGS -fPIC" make \ + && make install clean \ + && rm -rf /tmp/libvterm-0.3.3 +#docker build . -t libvterm && docker run -v $PWD:/build --rm libvterm /bin/ash -c "cd /build/;gcc terminal.c -Wl,-Bstatic -lvterm -Wl,-Bdynamic -o terminal.so -shared -fPIC" diff --git a/extensions/terminal/ffi.lisp b/extensions/terminal/ffi.lisp new file mode 100644 index 000000000..24d3b161a --- /dev/null +++ b/extensions/terminal/ffi.lisp @@ -0,0 +1,305 @@ +(defpackage :lem-terminal/ffi + (:use :cl)) +(in-package :lem-terminal/ffi) + +(pushnew (asdf:system-relative-pathname + :lem-terminal (format nil + "~(lib/~A/~A/~)" + (uiop:operating-system) + (uiop:architecture))) + cffi:*foreign-library-directories* + :test #'uiop:pathname-equal) + +(cffi:define-foreign-library terminal + (:unix "terminal.so")) + +(ignore-errors + (cffi:use-foreign-library terminal)) + +(defconstant VTERM_KEY_NONE 0) +(defconstant VTERM_KEY_ENTER 1) +(defconstant VTERM_KEY_TAB 2) +(defconstant VTERM_KEY_BACKSPACE 3) +(defconstant VTERM_KEY_ESCAPE 4) +(defconstant VTERM_KEY_UP 5) +(defconstant VTERM_KEY_DOWN 6) +(defconstant VTERM_KEY_LEFT 7) +(defconstant VTERM_KEY_RIGHT 8) +(defconstant VTERM_KEY_INS 9) +(defconstant VTERM_KEY_DEL 10) +(defconstant VTERM_KEY_HOME 11) +(defconstant VTERM_KEY_END 12) +(defconstant VTERM_KEY_PAGEUP 13) +(defconstant VTERM_KEY_PAGEDOWN 14) +(defconstant VTERM_KEY_FUNCTION_0 256) +(defconstant VTERM_KEY_FUNCTION_MAX 511) +(defconstant VTERM_KEY_KP_0 512) +(defconstant VTERM_KEY_KP_1 513) +(defconstant VTERM_KEY_KP_2 514) +(defconstant VTERM_KEY_KP_3 515) +(defconstant VTERM_KEY_KP_4 516) +(defconstant VTERM_KEY_KP_5 517) +(defconstant VTERM_KEY_KP_6 518) +(defconstant VTERM_KEY_KP_7 519) +(defconstant VTERM_KEY_KP_8 520) +(defconstant VTERM_KEY_KP_9 521) +(defconstant VTERM_KEY_KP_MULT 522) +(defconstant VTERM_KEY_KP_PLUS 523) +(defconstant VTERM_KEY_KP_COMMA 524) +(defconstant VTERM_KEY_KP_MINUS 525) +(defconstant VTERM_KEY_KP_PERIOD 526) +(defconstant VTERM_KEY_KP_DIVIDE 527) +(defconstant VTERM_KEY_KP_ENTER 528) +(defconstant VTERM_KEY_KP_EQUAL 529) +(defconstant VTERM_KEY_MAX 530) +(defconstant VTERM_N_KEYS 530) + +(defconstant VTERM_MOD_NONE #x00) +(defconstant VTERM_MOD_SHIFT #x01) +(defconstant VTERM_MOD_ALT #x02) +(defconstant VTERM_MOD_CTRL #x04) + +;; Define a type corresponding to char*[], where the array is null-terminated +(cffi:define-foreign-type string-of-strings () + () + (:actual-type :pointer)) +(cffi:define-parse-method string-of-strings () + (make-instance 'string-of-strings)) +;; It's inefficient to store the array of string pointers in both the second return +;; value of translate-to-foreign and (necessarily) foreign memory- but I can't get +;; the "efficient" way to work. +;; Also this likely isn't performance sensitive. Like at all. +(defmethod cffi:translate-to-foreign (val (type string-of-strings)) + (let ((string-pointers (map 'vector (lambda (str) (cffi:foreign-string-alloc str)) val))) + (values (cffi:foreign-alloc :pointer :null-terminated-p t :initial-contents string-pointers) + string-pointers))) +(defmethod cffi:free-translated-object (pointer (type string-of-strings) string-pointers) + (map nil (lambda (p) (cffi:foreign-string-free p)) + string-pointers) + (cffi:foreign-free pointer)) + +(cffi:defcfun ("terminal_new" %terminal-new) :pointer + (id :int) + (rows :int) + (cols :int) + (program :string) + (argv (string-of-strings)) + (cb_damage :pointer) + (cb_moverect :pointer) + (cb_movecursor :pointer) + (cb_settermprop :pointer) + (cb_bell :pointer) + (cb_resize :pointer) + (cb_sb_pushline :pointer) + (cb_sb_popline :pointer)) + +(defun terminal-new (directory id rows cols) + (let* ((shell (or (uiop:getenv "SHELL") "/bin/bash")) + (argv (list shell "-c" (concatenate 'string "cd " directory "; " shell)))) + (%terminal-new id + rows + cols + shell + argv + (cffi:callback cb-damage) + (cffi:callback cb-moverect) + (cffi:callback cb-movecursor) + (cffi:callback cb-settermprop) + (cffi:callback cb-bell) + (cffi:callback cb-resize) + (cffi:callback cb-sb-pushline) + (cffi:callback cb-sb-popline)))) + +(cffi:defcfun ("terminal_delete" terminal-delete) :void + (terminal :pointer)) + +(cffi:defcfun ("terminal_input_char" terminal-input-char) :void + (terminal :pointer) + (c :uint32) + (mod :int)) + +(cffi:defcfun ("terminal_input_key" terminal-input-key) :void + (terminal :pointer) + (key :int) + (mod :int)) + +(cffi:defcfun ("terminal_process_input_wait" terminal-process-input-wait) :void + (terminal :pointer)) + +(cffi:defcfun ("terminal_process_input_nonblock" terminal-process-input-nonblock) :bool + (terminal :pointer)) + +(cffi:defcfun ("terminal_process_input" terminal-process-input) :void + (terminal :pointer)) + +(cffi:defcfun ("terminal_query_cell" terminal-query-cell) :pointer + (terminal :pointer) + (x :int) + (y :int)) + +(cffi:defcfun ("terminal_last_cell_chars" terminal-last-cell-chars) :pointer + (terminal :pointer)) + +(cffi:defcfun ("terminal_last_cell_width" terminal-last-cell-width) :int + (terminal :pointer)) + +(cffi:defcfun ("terminal_last_cell_attrs_bold" terminal-last-cell-attrs-bold) :unsigned-int + (terminal :pointer)) + +(cffi:defcfun ("terminal_last_cell_attrs_underline" terminal-last-cell-attrs-underline) :unsigned-int + (terminal :pointer)) + +(cffi:defcfun ("terminal_last_cell_attrs_italic" terminal-last-cell-attrs-italic) :unsigned-int + (terminal :pointer)) + +(cffi:defcfun ("terminal_last_cell_attrs_blink" terminal-last-cell-attrs-blink) :unsigned-int + (terminal :pointer)) + +(cffi:defcfun ("terminal_last_cell_attrs_reverse" terminal-last-cell-attrs-reverse) :unsigned-int + (terminal :pointer)) + +(cffi:defcfun ("terminal_last_cell_attrs_conceal" terminal-last-cell-attrs-conceal) :unsigned-int + (terminal :pointer)) + +(cffi:defcfun ("terminal_last_cell_attrs_strike" terminal-last-cell-attrs-strike) :unsigned-int + (terminal :pointer)) + +(cffi:defcfun ("terminal_last_cell_attrs_font" terminal-last-cell-attrs-font) :unsigned-int + (terminal :pointer)) + +(cffi:defcfun ("terminal_last_cell_attrs_dwl" terminal-last-cell-attrs-dwl) :unsigned-int + (terminal :pointer)) + +(cffi:defcfun ("terminal_last_cell_attrs_dhl" terminal-last-cell-attrs-dhl) :unsigned-int + (terminal :pointer)) + +(cffi:defcfun ("terminal_last_cell_attrs_small" terminal-last-cell-attrs-small) :unsigned-int + (terminal :pointer)) + +(cffi:defcfun ("terminal_last_cell_attrs_baseline" terminal-last-cell-attrs-baseline) :unsigned-int + (terminal :pointer)) + +(cffi:defcfun ("terminal_last_cell_fg_red" terminal-last-cell-fg-red) :uint8 + (terminal :pointer)) + +(cffi:defcfun ("terminal_last_cell_fg_green" terminal-last-cell-fg-green) :uint8 + (terminal :pointer)) + +(cffi:defcfun ("terminal_last_cell_fg_blue" terminal-last-cell-fg-blue) :uint8 + (terminal :pointer)) + +(cffi:defcfun ("terminal_last_cell_bg_red" terminal-last-cell-bg-red) :uint8 + (terminal :pointer)) + +(cffi:defcfun ("terminal_last_cell_bg_green" terminal-last-cell-bg-green) :uint8 + (terminal :pointer)) + +(cffi:defcfun ("terminal_last_cell_bg_blue" terminal-last-cell-bg-blue) :uint8 + (terminal :pointer)) + +(cffi:defcfun ("terminal_cursor_row" terminal-cursor-row) :int + (terminal :pointer)) + +(cffi:defcfun ("terminal_cursor_col" terminal-cursor-col) :int + (terminal :pointer)) + +(cffi:defcfun ("terminal_resize" terminal-resize) :void + (terminal :pointer) + (rows :int) + (cols :int)) + +(cffi:defcstruct vterm-rect + (start-row :int) + (start-col :int) + (end-row :int) + (end-col :int)) + +(cffi:defcstruct vterm-pos + (row :int) + (col :int)) + +(cffi:defcenum vterm-prop + (:VTERM_PROP_CURSORVISIBLE 1) ; bool + :VTERM_PROP_CURSORBLINK ; bool + :VTERM_PROP_ALTSCREEN ; bool + :VTERM_PROP_TITLE ; string + :VTERM_PROP_ICONNAME ; string + :VTERM_PROP_REVERSE ; bool + :VTERM_PROP_CURSORSHAPE ; number + :VTERM_PROP_MOUSE ; number + :VTERM_PROP_FOCUSREPORT ; bool + ) + +(defvar *damage-callback* nil) +(defvar *moverect-callback* nil) +(defvar *movecursor-callback* nil) +(defvar *settermprop-callback* nil) +(defvar *bell-callback* nil) +(defvar *resize-callback* nil) +(defvar *sb-pushline-callback* nil) +(defvar *sb-popline-callback* nil) + +(defun set-callbacks (&key damage + moverect + movecursor + settermprop + bell + resize + sb-pushline + sb-popline) + (setf *damage-callback* damage + *moverect-callback* moverect + *movecursor-callback* movecursor + *settermprop-callback* settermprop + *bell-callback* bell + *resize-callback* resize + *sb-pushline-callback* sb-pushline + *sb-popline-callback* sb-popline) + (values)) + +(cffi:defcallback cb-damage :int ((rect (:pointer (:struct vterm-rect))) (id :int)) + (when *damage-callback* + (funcall *damage-callback* rect id)) + 0) + +(cffi:defcallback cb-moverect :int ((dest (:pointer (:struct vterm-rect))) + (src (:pointer (:struct vterm-rect))) + (id :int)) + (when *moverect-callback* + (funcall *moverect-callback* dest src id)) + 0) + +(cffi:defcallback cb-movecursor :int ((pos (:pointer (:struct vterm-pos))) + (oldpos (:pointer (:struct vterm-pos))) + (visible :int) + (id :int)) + (when *movecursor-callback* + (funcall *movecursor-callback* pos oldpos visible id)) + 0) + +(cffi:defcallback cb-settermprop :int ((prop vterm-prop) + (val :pointer) + (id :int)) + (when *settermprop-callback* + (funcall *settermprop-callback* prop val id)) + 0) + +(cffi:defcallback cb-bell :int ((id :int)) + (when *bell-callback* + (funcall *bell-callback* id)) + 0) + +(cffi:defcallback cb-resize :int ((rows :int) (cols :int) (id :int)) + (when *resize-callback* + (funcall *resize-callback* rows cols id)) + 0) + +(cffi:defcallback cb-sb-pushline :int ((cols :int) (cells :pointer) (id :int)) + (when *sb-pushline-callback* + (funcall *sb-pushline-callback* cols cells id)) + 0) + +(cffi:defcallback cb-sb-popline :int ((cols :int) (cells :pointer) (id :int)) + (when *sb-popline-callback* + (funcall *sb-popline-callback* cols cells id)) + 0) diff --git a/extensions/terminal/lem-terminal.asd b/extensions/terminal/lem-terminal.asd new file mode 100644 index 000000000..f77d4e3fd --- /dev/null +++ b/extensions/terminal/lem-terminal.asd @@ -0,0 +1,6 @@ +(defsystem "lem-terminal" + :depends-on ("lem") + :serial t + :components ((:file "ffi") + (:file "terminal") + (:file "terminal-mode"))) diff --git a/extensions/terminal/lib/linux/x64/terminal.so b/extensions/terminal/lib/linux/x64/terminal.so new file mode 100755 index 000000000..6ad184d00 Binary files /dev/null and b/extensions/terminal/lib/linux/x64/terminal.so differ diff --git a/extensions/terminal/lib/macosx/arm64/terminal.so b/extensions/terminal/lib/macosx/arm64/terminal.so new file mode 100755 index 000000000..391784bdf Binary files /dev/null and b/extensions/terminal/lib/macosx/arm64/terminal.so differ diff --git a/extensions/terminal/terminal-mode.lisp b/extensions/terminal/terminal-mode.lisp new file mode 100644 index 000000000..4deb2c90c --- /dev/null +++ b/extensions/terminal/terminal-mode.lisp @@ -0,0 +1,205 @@ +(uiop:define-package :lem-terminal/terminal-mode + (:use :cl :lem) + (:local-nicknames (:ffi :lem-terminal/ffi)) + (:local-nicknames (:terminal :lem-terminal/terminal))) +(in-package :lem-terminal/terminal-mode) + +;; FIXME: Think of a better name +(defvar *bypass-commands* + '(next-window + previous-window + split-active-window-vertically + split-active-window-horizontally + delete-other-windows + delete-active-window + select-buffer + kill-buffer + find-file + execute-command + terminal-resize + terminal-copy-mode-on + lem-core:: + lem-core:: + lem/frame-multiplexer:frame-multiplexer-advice)) + +(define-major-mode terminal-mode () + (:name "Terminal" + :keymap *terminal-mode-keymap*) + (setf (buffer-read-only-p (current-buffer)) t) + (setf (variable-value 'highlight-line :buffer (current-buffer)) nil) + (setf (variable-value 'line-wrap :buffer (current-buffer)) nil) + (setf (variable-value 'lem/show-paren:enable :buffer (current-buffer)) nil) + (add-hook (variable-value 'kill-buffer-hook :buffer (current-buffer)) 'on-kill-buffer) + (alexandria:when-let (terminal (get-current-terminal)) + (terminal:copy-mode-off terminal))) + +(define-major-mode terminal-copy-mode () + (:name "Terminal (Copy)" + :keymap *terminal-copy-mode-keymap*) + (setf (buffer-read-only-p (current-buffer)) t) + (setf (variable-value 'highlight-line :buffer (current-buffer)) nil) + (setf (variable-value 'line-wrap :buffer (current-buffer)) nil) + (setf (variable-value 'lem/show-paren:enable :buffer (current-buffer)) nil) + (add-hook (variable-value 'kill-buffer-hook :buffer (current-buffer)) 'on-kill-buffer) + (alexandria:when-let (terminal (get-current-terminal)) + (terminal:copy-mode-on terminal))) + +(define-key *terminal-mode-keymap* 'self-insert 'terminal-input) +(define-key *terminal-mode-keymap* 'undefined-key 'terminal-input) +(define-key *terminal-mode-keymap* "C-h" 'terminal-input) +(define-key *terminal-mode-keymap* "C-x [" 'terminal-copy-mode-on) +(define-key *terminal-copy-mode-keymap* "Escape" 'terminal-copy-mode-off) + +(defun buffer-terminal (buffer) + (buffer-value buffer 'terminal)) + +(defun (setf buffer-terminal) (terminal buffer) + (setf (buffer-value buffer 'terminal) terminal)) + +(defun make-terminal-buffer (buffer-directory) + (declare (type (string) buffer-directory)) + (let* ((buffer (make-buffer (unique-buffer-name "*Terminal*") :enable-undo-p nil)) + (terminal (terminal:create :cols 80 :rows 24 :buffer buffer + :directory buffer-directory))) + (setf (buffer-terminal buffer) terminal) + (change-buffer-mode buffer 'terminal-mode) + buffer)) + +(defun on-kill-buffer (buffer) + (let ((terminal (buffer-terminal buffer))) + (when terminal + (terminal:destroy terminal)))) + +(defun create-terminal (buffer-directory) + (declare (type (string) buffer-directory)) + (let* ((buffer (make-terminal-buffer buffer-directory)) + (window (pop-to-buffer buffer))) + (resize-terminal (buffer-terminal buffer) window) + (setf (current-window) window))) + +(define-command terminal (always-create-terminal-p) (:universal-nil) + (labels ((new-terminal () + (create-terminal (buffer-directory (current-buffer))))) + (if always-create-terminal-p + (new-terminal) + (alexandria:if-let (buffer (terminal:find-terminal-buffer)) + (setf (current-window) (pop-to-buffer buffer)) + (new-terminal))))) + +(defun get-current-terminal () + (buffer-terminal (current-buffer))) + +(define-command terminal-input () () + (let ((terminal (get-current-terminal)) + (keyseq (last-read-key-sequence))) + (dolist (key keyseq) + (let ((mod (logior (if (key-ctrl key) ffi::vterm_mod_ctrl 0) + (if (key-meta key) ffi::vterm_mod_alt 0) + (if (key-shift key) ffi::vterm_mod_shift 0)))) + (alexandria:switch ((key-sym key) :test #'equal) + ("Return" (terminal:input-key terminal ffi::vterm_key_enter :mod mod)) + ("Backspace" (terminal:input-key terminal ffi::vterm_key_backspace :mod mod)) + ("Tab" (terminal:input-key terminal ffi::vterm_key_tab :mod mod)) + ("Escape" (terminal:input-key terminal ffi::vterm_key_escape :mod mod)) + ("Up" (terminal:input-key terminal ffi::vterm_key_up :mod mod)) + ("Down" (terminal:input-key terminal ffi::vterm_key_down :mod mod)) + ("Left" (terminal:input-key terminal ffi::vterm_key_left :mod mod)) + ("Right" (terminal:input-key terminal ffi::vterm_key_right :mod mod)) + ("Insert" (terminal:input-key terminal ffi::vterm_key_ins :mod mod)) + ("Delete" (terminal:input-key terminal ffi::vterm_key_del :mod mod)) + ("Home" (terminal:input-key terminal ffi::vterm_key_home :mod mod)) + ("End" (terminal:input-key terminal ffi::vterm_key_end :mod mod)) + ("PageUp" (terminal:input-key terminal ffi::vterm_key_pageup :mod mod)) + ("PageDown" (terminal:input-key terminal ffi::vterm_key_pagedown :mod mod)) + ("F1" (terminal:input-key terminal (+ 1 ffi::vterm_key_function_0) :mod mod)) + ("F2" (terminal:input-key terminal (+ 2 ffi::vterm_key_function_0) :mod mod)) + ("F3" (terminal:input-key terminal (+ 3 ffi::vterm_key_function_0) :mod mod)) + ("F4" (terminal:input-key terminal (+ 4 ffi::vterm_key_function_0) :mod mod)) + ("F5" (terminal:input-key terminal (+ 5 ffi::vterm_key_function_0) :mod mod)) + ("F6" (terminal:input-key terminal (+ 6 ffi::vterm_key_function_0) :mod mod)) + ("F7" (terminal:input-key terminal (+ 7 ffi::vterm_key_function_0) :mod mod)) + ("F8" (terminal:input-key terminal (+ 8 ffi::vterm_key_function_0) :mod mod)) + ("F9" (terminal:input-key terminal (+ 9 ffi::vterm_key_function_0) :mod mod)) + ("F10" (terminal:input-key terminal (+ 10 ffi::vterm_key_function_0) :mod mod)) + ("F11" (terminal:input-key terminal (+ 11 ffi::vterm_key_function_0) :mod mod)) + ("F12" (terminal:input-key terminal (+ 12 ffi::vterm_key_function_0) :mod mod)) + ("Space" (terminal:input-character terminal #\Space :mod mod)) + (otherwise + (when (= 1 (length (key-sym key))) + (terminal:input-character terminal (char (key-sym key) 0) :mod mod)))))))) + +(defmacro define-terminal-key-command (name keyspec vterm-key) + (alexandria:with-unique-names (terminal) + `(progn + (define-key *terminal-mode-keymap* ,keyspec ',name) + (define-command ,name () () + (let ((,terminal (get-current-terminal))) + (terminal:input-key ,terminal ,vterm-key)))))) + +(define-terminal-key-command terminal-key-return "Return" ffi::vterm_key_enter) +(define-terminal-key-command terminal-key-backspace "Backspace" ffi::vterm_key_backspace) +(define-terminal-key-command terminal-key-tab "Tab" ffi::vterm_key_tab) +(define-terminal-key-command terminal-key-escape "Escape" ffi::vterm_key_escape) +(define-terminal-key-command terminal-key-up "Up" ffi::vterm_key_up) +(define-terminal-key-command terminal-key-down "Down" ffi::vterm_key_down) +(define-terminal-key-command terminal-key-left "Left" ffi::vterm_key_left) +(define-terminal-key-command terminal-key-right "Right" ffi::vterm_key_right) +(define-terminal-key-command terminal-key-insert "Insert" ffi::vterm_key_ins) +(define-terminal-key-command terminal-key-delete "Delete" ffi::vterm_key_del) +(define-terminal-key-command terminal-key-home "Home" ffi::vterm_key_home) +(define-terminal-key-command terminal-key-end "End" ffi::vterm_key_end) +(define-terminal-key-command terminal-key-pageup "PageUp" ffi::vterm_key_pageup) +(define-terminal-key-command terminal-key-pagedown "PageDown" ffi::vterm_key_pagedown) +(define-terminal-key-command terminal-key-f1 "F1" (+ 1 ffi::vterm_key_function_0)) +(define-terminal-key-command terminal-key-f2 "F2" (+ 2 ffi::vterm_key_function_0)) +(define-terminal-key-command terminal-key-f3 "F3" (+ 3 ffi::vterm_key_function_0)) +(define-terminal-key-command terminal-key-f4 "F4" (+ 4 ffi::vterm_key_function_0)) +(define-terminal-key-command terminal-key-f5 "F5" (+ 5 ffi::vterm_key_function_0)) +(define-terminal-key-command terminal-key-f6 "F6" (+ 6 ffi::vterm_key_function_0)) +(define-terminal-key-command terminal-key-f7 "F7" (+ 7 ffi::vterm_key_function_0)) +(define-terminal-key-command terminal-key-f8 "F8" (+ 8 ffi::vterm_key_function_0)) +(define-terminal-key-command terminal-key-f9 "F9" (+ 9 ffi::vterm_key_function_0)) +(define-terminal-key-command terminal-key-f10 "F10" (+ 10 ffi::vterm_key_function_0)) +(define-terminal-key-command terminal-key-f11 "F11" (+ 11 ffi::vterm_key_function_0)) +(define-terminal-key-command terminal-key-f12 "F12" (+ 12 ffi::vterm_key_function_0)) + +(defun adjust-current-point () + (alexandria:if-let ((terminal (get-current-terminal))) + (terminal:adjust-point terminal))) + +(define-command terminal-copy-mode-on () () + (terminal-copy-mode)) + +(define-command terminal-copy-mode-off () () + (terminal-mode) + (adjust-current-point)) + +(defmethod execute ((mode terminal-mode) command argment) + (if (member command *bypass-commands* :test #'typep) + (call-next-method) + (terminal-input))) + +(defun resize-terminal (terminal window) + (terminal:resize terminal + :rows (1- (window-height window)) + :cols (window-width window))) + +(define-command terminal-resize () () + (alexandria:when-let ((terminal (get-current-terminal)) + (window (current-window))) + (resize-terminal terminal window))) + +(defmethod lem-core:paste-using-mode ((mode terminal-mode) string) + (let ((terminal (get-current-terminal))) + (loop :for c :across string + :do (terminal:input-character terminal c)))) + +(defun on-window-size-change (window) + (alexandria:when-let (terminal (buffer-terminal (window-buffer window))) + (resize-terminal terminal window))) + +(add-hook *window-size-change-functions* + 'on-window-size-change) + +(add-hook *post-command-hook* + 'terminal-resize) diff --git a/extensions/terminal/terminal.c b/extensions/terminal/terminal.c new file mode 100644 index 000000000..abba4a7d8 --- /dev/null +++ b/extensions/terminal/terminal.c @@ -0,0 +1,454 @@ +// linux check Dockerfile or Dockerfile.musl +// osx gcc terminal.c -I/opt/homebrew/include -L/opt/homebrew/lib -lvterm -o terminal.so -shared -fPIC + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ + #include + #include +#else + #include +#endif + +/// +typedef struct { + pid_t pid; + int fd; +} run_shell_result; + +static run_shell_result run_shell(int rows, int cols, const char *program, char* const argv[]) +{ + int fd; + struct winsize win = { rows, cols, 0, 0 }; + pid_t pid = forkpty(&fd, NULL, NULL, &win); + assert(pid >= 0); + if (pid == 0) { + setenv("TERM", "xterm-256color", 1); + assert(execvp(program, argv) >= 0); + } + + run_shell_result result = { pid, fd }; + return result; +} + +/// +struct terminal { + int id; + VTerm *vterm; + VTermScreen *screen; + int fd; + int pid; + VTermScreenCell lastCell; + VTermPos cursorPos; + int cursorVisible; + + int (*cb_damage)(VTermRect *rect, int id); + int (*cb_moverect)(VTermRect *dest, VTermRect *src, int id); + int (*cb_movecursor)(VTermPos *pos, VTermPos *oldpos, int visible, int id); + int (*cb_settermprop)(VTermProp prop, VTermValue *val, int id); + int (*cb_bell)(int id); + int (*cb_resize)(int rows, int cols, int id); + int (*cb_sb_pushline)(int cols, const VTermScreenCell *cells, int id); + int (*cb_sb_popline)(int cols, VTermScreenCell *cells, int id); + int (*cb_sb_clear)(int id); // TODO +}; + +static int cb_damage(VTermRect rect, void *user) +{ + struct terminal *terminal = ((struct terminal *)user); + terminal->cb_damage(&rect, terminal->id); + return 0; +} + +static int cb_moverect(VTermRect dest, VTermRect src, void *user) +{ + struct terminal *terminal = ((struct terminal *)user); + terminal->cb_moverect(&dest, &src, terminal->id); + return 0; +} + +static int cb_movecursor(VTermPos pos, VTermPos oldpos, int visible, + void *user) +{ + struct terminal *terminal = ((struct terminal *)user); + + // TODO + terminal->cursorPos = pos; + terminal->cursorVisible = visible; + + terminal->cb_movecursor(&pos, &oldpos, visible, terminal->id); + return 0; +} + +static int cb_settermprop(VTermProp prop, VTermValue *val, void *user) +{ + struct terminal *terminal = ((struct terminal *)user); + terminal->cb_settermprop(prop, val, terminal->id); + return 0; +} + +static int cb_bell(void *user) +{ + struct terminal *terminal = ((struct terminal *)user); + terminal->cb_bell(terminal->id); + return 0; +} + +static int cb_resize(int rows, int cols, void *user) +{ + struct terminal *terminal = ((struct terminal *)user); + terminal->cb_resize(rows, cols, terminal->id); + return 0; +} + +static int cb_sb_pushline(int cols, const VTermScreenCell *cells, + void *user) +{ + struct terminal *terminal = ((struct terminal *)user); + terminal->cb_sb_pushline(cols, cells, terminal->id); + return 0; +} + +static int cb_sb_popline(int cols, VTermScreenCell *cells, void *user) +{ + struct terminal *terminal = ((struct terminal *)user); + terminal->cb_sb_popline(cols, cells, terminal->id); + return 0; +} + +static void output_callback(const char *s, size_t len, void *user) +{ + struct terminal *terminal = (struct terminal *) user; + write(terminal->fd, s, len); +} + +VTermScreenCallbacks screen_callbacks = { + cb_damage, + cb_moverect, + cb_movecursor, + cb_settermprop, + cb_bell, + cb_resize, + cb_sb_pushline, + cb_sb_popline, +}; + +struct terminal *terminal_new(int id, + int rows, + int cols, + const char *program, + char* const argv[], + void *cb_damage, + void *cb_moverect, + void *cb_movecursor, + void *cb_settermprop, + void *cb_bell, + void *cb_resize, + void *cb_sb_pushline, + void *cb_sb_popline) +{ + run_shell_result result = run_shell(rows, cols, program, argv); + + VTerm *vterm = vterm_new(rows, cols); + vterm_set_utf8(vterm, 1); + + VTermScreen *screen = vterm_obtain_screen(vterm); + + struct terminal *terminal = malloc(sizeof(struct terminal)); + if (terminal == NULL) { + return NULL; + } + terminal->id = id; + terminal->screen = screen; + terminal->vterm = vterm; + terminal->fd = result.fd; + terminal->pid = result.pid; + + terminal->cb_damage = cb_damage; + terminal->cb_moverect = cb_moverect; + terminal->cb_movecursor = cb_movecursor; + terminal->cb_settermprop = cb_settermprop; + terminal->cb_bell = cb_bell; + terminal->cb_resize = cb_resize; + terminal->cb_sb_pushline = cb_sb_pushline; + terminal->cb_sb_popline = cb_sb_popline; + + vterm_output_set_callback(vterm, output_callback, (void *) terminal); + vterm_screen_set_callbacks(screen, &screen_callbacks, terminal); + vterm_screen_reset(screen, 1); + + return terminal; +} + +void terminal_delete(struct terminal *terminal) +{ + vterm_free(terminal->vterm); + free(terminal); +} + +void terminal_input_char(struct terminal *terminal, uint32_t c, + VTermModifier mod) +{ + vterm_keyboard_unichar(terminal->vterm, c, mod); +} + +void terminal_input_key(struct terminal *terminal, VTermKey key, + VTermModifier mod) +{ + vterm_keyboard_key(terminal->vterm, key, mod); +} + +void terminal_process_input_wait(struct terminal *terminal) +{ + int fd = terminal->fd; + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + select(fd + 1, &readfds, NULL, NULL, NULL); +} + +bool terminal_process_input_nonblock(struct terminal *terminal) +{ + int fd = terminal->fd; + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + struct timeval timeout = { 0, 0 }; + if (select(fd + 1, &readfds, NULL, NULL, &timeout) > 0) { + char buf[4096]; + ssize_t size = read(fd, buf, sizeof(buf)); + if (size > 0) { + vterm_input_write(terminal->vterm, buf, size); + return true; + } + } + return false; +} + +void terminal_process_input(struct terminal *terminal) +{ + int fd = terminal->fd; + char buf[4096]; + ssize_t size = read(fd, buf, sizeof(buf)); + if (size > 0) { + vterm_input_write(terminal->vterm, buf, size); + } +} + +VTermScreenCell *terminal_query_cell(struct terminal *terminal, int x, + int y) +{ + VTermPos pos = { row: y, col:x }; + VTermScreenCell cell; + vterm_screen_get_cell(terminal->screen, pos, &cell); + if (VTERM_COLOR_IS_INDEXED(&cell.fg)) { + vterm_screen_convert_color_to_rgb(terminal->screen, &cell.fg); + } + if (VTERM_COLOR_IS_INDEXED(&cell.bg)) { + vterm_screen_convert_color_to_rgb(terminal->screen, &cell.bg); + } + terminal->lastCell = cell; + return &terminal->lastCell; +} + +int *terminal_last_cell_chars(struct terminal *terminal) +{ + return terminal->lastCell.chars; +} + +int terminal_last_cell_width(struct terminal *terminal) +{ + return terminal->lastCell.width; +} + +unsigned int terminal_last_cell_attrs_bold(struct terminal *terminal) +{ + return terminal->lastCell.attrs.bold; +} + +unsigned int terminal_last_cell_attrs_underline(struct terminal *terminal) +{ + return terminal->lastCell.attrs.underline; +} + +unsigned int terminal_last_cell_attrs_italic(struct terminal *terminal) +{ + return terminal->lastCell.attrs.italic; +} + +unsigned int terminal_last_cell_attrs_blink(struct terminal *terminal) +{ + return terminal->lastCell.attrs.blink; +} + +unsigned int terminal_last_cell_attrs_reverse(struct terminal *terminal) +{ + return terminal->lastCell.attrs.reverse; +} + +unsigned int terminal_last_cell_attrs_conceal(struct terminal *terminal) +{ + return terminal->lastCell.attrs.conceal; +} + +unsigned int terminal_last_cell_attrs_strike(struct terminal *terminal) +{ + return terminal->lastCell.attrs.strike; +} + +unsigned int terminal_last_cell_attrs_font(struct terminal *terminal) +{ + return terminal->lastCell.attrs.font; +} + +unsigned int terminal_last_cell_attrs_dwl(struct terminal *terminal) +{ + return terminal->lastCell.attrs.dwl; +} + +unsigned int terminal_last_cell_attrs_dhl(struct terminal *terminal) +{ + return terminal->lastCell.attrs.dhl; +} + +unsigned int terminal_last_cell_attrs_small(struct terminal *terminal) +{ + return terminal->lastCell.attrs.small; +} + +unsigned int terminal_last_cell_attrs_baseline(struct terminal *terminal) +{ + return terminal->lastCell.attrs.baseline; +} + +uint8_t terminal_last_cell_fg_red(struct terminal *terminal) +{ + return terminal->lastCell.fg.rgb.red; +} + +uint8_t terminal_last_cell_fg_green(struct terminal *terminal) +{ + return terminal->lastCell.fg.rgb.green; +} + +uint8_t terminal_last_cell_fg_blue(struct terminal *terminal) +{ + return terminal->lastCell.fg.rgb.blue; +} + +uint8_t terminal_last_cell_bg_red(struct terminal *terminal) +{ + return terminal->lastCell.bg.rgb.red; +} + +uint8_t terminal_last_cell_bg_green(struct terminal *terminal) +{ + return terminal->lastCell.bg.rgb.green; +} + +uint8_t terminal_last_cell_bg_blue(struct terminal *terminal) +{ + return terminal->lastCell.bg.rgb.blue; +} + +int terminal_cursor_row(struct terminal *terminal) +{ + return terminal->cursorPos.row; +} + +int terminal_cursor_col(struct terminal *terminal) +{ + return terminal->cursorPos.col; +} + +void terminal_resize(struct terminal *terminal, int rows, int cols) +{ + vterm_set_size(terminal->vterm, rows, cols); + vterm_screen_flush_damage(terminal->screen); + + struct winsize win = { 0 }; + win.ws_row = rows; + win.ws_col = cols; + ioctl(terminal->fd, TIOCSWINSZ, &win); +} + +#if 0 +int main(void) +{ + int cols = 80, rows = 24; + + struct terminal *terminal = terminal_new(rows, cols); + terminal_process_input(terminal); + terminal_input_char(terminal, 'l', 0); + terminal_process_input(terminal); + terminal_input_char(terminal, 's', 0); + terminal_process_input(terminal); + terminal_input_key(terminal, VTERM_KEY_ENTER, 0); + terminal_process_input(terminal); + terminal_process_input(terminal); + terminal_process_input(terminal); + + for (int row = 0; row < rows; row++) { + for (int col = 0; col < cols; col++) { + terminal_query_cell(terminal, col, row); + printf("%c", terminal->lastCell.chars[0]); + } + putchar('\n'); + } +} +#endif + +#if 0 +int main(void) +{ + printf("VTERM_KEY_NONE: %d\n", VTERM_KEY_NONE); + + printf("VTERM_KEY_ENTER: %d\n", VTERM_KEY_ENTER); + printf("VTERM_KEY_TAB: %d\n", VTERM_KEY_TAB); + printf("VTERM_KEY_BACKSPACE: %d\n", VTERM_KEY_BACKSPACE); + printf("VTERM_KEY_ESCAPE: %d\n", VTERM_KEY_ESCAPE); + + printf("VTERM_KEY_UP: %d\n", VTERM_KEY_UP); + printf("VTERM_KEY_DOWN: %d\n", VTERM_KEY_DOWN); + printf("VTERM_KEY_LEFT: %d\n", VTERM_KEY_LEFT); + printf("VTERM_KEY_RIGHT: %d\n", VTERM_KEY_RIGHT); + + printf("VTERM_KEY_INS: %d\n", VTERM_KEY_INS); + printf("VTERM_KEY_DEL: %d\n", VTERM_KEY_DEL); + printf("VTERM_KEY_HOME: %d\n", VTERM_KEY_HOME); + printf("VTERM_KEY_END: %d\n", VTERM_KEY_END); + printf("VTERM_KEY_PAGEUP: %d\n", VTERM_KEY_PAGEUP); + printf("VTERM_KEY_PAGEDOWN: %d\n", VTERM_KEY_PAGEDOWN); + + printf("VTERM_KEY_FUNCTION_0: %d\n", VTERM_KEY_FUNCTION_0); + printf("VTERM_KEY_FUNCTION_MAX: %d\n", VTERM_KEY_FUNCTION_MAX); + + printf("VTERM_KEY_KP_0: %d\n", VTERM_KEY_KP_0); + printf("VTERM_KEY_KP_1: %d\n", VTERM_KEY_KP_1); + printf("VTERM_KEY_KP_2: %d\n", VTERM_KEY_KP_2); + printf("VTERM_KEY_KP_3: %d\n", VTERM_KEY_KP_3); + printf("VTERM_KEY_KP_4: %d\n", VTERM_KEY_KP_4); + printf("VTERM_KEY_KP_5: %d\n", VTERM_KEY_KP_5); + printf("VTERM_KEY_KP_6: %d\n", VTERM_KEY_KP_6); + printf("VTERM_KEY_KP_7: %d\n", VTERM_KEY_KP_7); + printf("VTERM_KEY_KP_8: %d\n", VTERM_KEY_KP_8); + printf("VTERM_KEY_KP_9: %d\n", VTERM_KEY_KP_9); + printf("VTERM_KEY_KP_MULT: %d\n", VTERM_KEY_KP_MULT); + printf("VTERM_KEY_KP_PLUS: %d\n", VTERM_KEY_KP_PLUS); + printf("VTERM_KEY_KP_COMMA: %d\n", VTERM_KEY_KP_COMMA); + printf("VTERM_KEY_KP_MINUS: %d\n", VTERM_KEY_KP_MINUS); + printf("VTERM_KEY_KP_PERIOD: %d\n", VTERM_KEY_KP_PERIOD); + printf("VTERM_KEY_KP_DIVIDE: %d\n", VTERM_KEY_KP_DIVIDE); + printf("VTERM_KEY_KP_ENTER: %d\n", VTERM_KEY_KP_ENTER); + printf("VTERM_KEY_KP_EQUAL: %d\n", VTERM_KEY_KP_EQUAL); + + printf("VTERM_KEY_MAX: %d\n", VTERM_KEY_MAX); + printf("VTERM_N_KEYS: %d\n", VTERM_N_KEYS); +} +#endif diff --git a/extensions/terminal/terminal.lisp b/extensions/terminal/terminal.lisp new file mode 100644 index 000000000..210e04463 --- /dev/null +++ b/extensions/terminal/terminal.lisp @@ -0,0 +1,237 @@ +(uiop:define-package :lem-terminal/terminal + (:use :cl :lem) + (:local-nicknames (:ffi :lem-terminal/ffi) + (:queue :lem/common/queue)) + (:export :find-terminal-buffer + :create + :destroy + :copy-mode-on + :copy-mode-off + :clear + :render + :update + :input-character + :input-key + :resize + :adjust-point)) +(in-package :lem-terminal/terminal) + +;;; id generator +(defvar *terminal-id-counter* 0) + +(defun generate-terminal-id () + (incf *terminal-id-counter*)) + +;;; terminals +(defvar *terminals* '()) + +(defun add-terminal (terminal) + (push terminal *terminals*)) + +(defun find-terminal-by-id (id) + (find id *terminals* :key #'terminal-id)) + +(defun remove-terminal (terminal) + (alexandria:deletef *terminals* terminal)) + +(defun find-terminal-buffer () + (alexandria:when-let (terminal (first *terminals*)) + (terminal-buffer terminal))) + +;;; terminal +(defclass terminal () + ((id :initarg :id + :reader terminal-id) + (viscus :initarg :viscus + :reader terminal-viscus) + (thread :initarg :thread + :accessor terminal-thread) + (buffer :initarg :buffer + :reader terminal-buffer) + (rows :initarg :rows + :accessor terminal-rows) + (cols :initarg :cols + :accessor terminal-cols) + (copy-mode :initform nil + :accessor terminal-copy-mode))) + +(defun update (terminal) + (process-input terminal) + (when (and (= 0 (event-queue-length)) + (not (terminal-copy-mode terminal))) + (render terminal) + (redraw-display))) + +(defun create (&key (rows (alexandria:required-argument :rows)) + (cols (alexandria:required-argument :cols)) + (buffer (alexandria:required-argument :buffer)) + (directory (alexandria:required-argument :directory))) + (declare (type (string) directory) + (type (integer) rows) + (type (integer) cols)) + (let* ((id (generate-terminal-id)) + (terminal + (make-instance 'terminal + :id id + :viscus (ffi::terminal-new directory id rows cols) + :buffer buffer + :rows rows + :cols cols))) + (let ((queue (queue:make-concurrent-queue))) + (setf (terminal-thread terminal) + (bt2:make-thread + (lambda () + (loop + (ffi::terminal-process-input-wait (terminal-viscus terminal)) + (send-event + (lambda () + ;; XXX: If this place is executed at the time the terminal is deleted, an error will occur. + (ignore-errors (update terminal)) + (queue:enqueue queue 1))) + (queue:dequeue queue))) + :name (format nil "Terminal ~D" id)))) + (add-terminal terminal) + terminal)) + +(defmethod destroy ((terminal terminal)) + (bt2:destroy-thread (terminal-thread terminal)) + (remove-terminal terminal) + (ffi::terminal-delete (terminal-viscus terminal))) + +(defmethod copy-mode-on ((terminal terminal)) + (setf (terminal-copy-mode terminal) t)) + +(defmethod copy-mode-off ((terminal terminal)) + (setf (terminal-copy-mode terminal) nil)) + +(defun get-foreground-color (viscus) + (let ((r (ffi::terminal-last-cell-fg-red viscus)) + (g (ffi::terminal-last-cell-fg-green viscus)) + (b (ffi::terminal-last-cell-fg-blue viscus))) + (make-color r g b))) + +(defun get-background-color (viscus) + (let ((r (ffi::terminal-last-cell-bg-red viscus)) + (g (ffi::terminal-last-cell-bg-green viscus)) + (b (ffi::terminal-last-cell-bg-blue viscus))) + (make-color r g b))) + +(defun fix-blue-color (color) + (if (lem/common/color::color-equal color (make-color 0 0 #xe0)) + (parse-color "#3465A4") + color)) + +(defun get-cell-attribute (viscus) + (let ((foreground (get-foreground-color viscus)) + (background (get-background-color viscus)) + (reverse (= 1 (ffi::terminal-last-cell-attrs-reverse viscus))) + (underline (= 1 (ffi::terminal-last-cell-attrs-underline viscus))) + (bold (= 1 (ffi::terminal-last-cell-attrs-bold viscus)))) + (make-attribute :foreground (fix-blue-color foreground) + :background (fix-blue-color background) + :reverse reverse + :bold bold + :underline underline))) + +(defun get-cell-character (viscus) + (let ((chars (ffi::terminal-last-cell-chars viscus))) + (when chars + (let ((char (ignore-errors (code-char (cffi:mem-ref chars :uint32 0))))) + char)))) + +(defmethod render ((terminal terminal)) + (let* ((viscus (terminal-viscus terminal)) + (rows (terminal-rows terminal)) + (cols (terminal-cols terminal)) + (buffer (terminal-buffer terminal)) + (point (buffer-point buffer))) + (with-buffer-read-only buffer nil + (erase-buffer buffer) + (loop :for row :from 0 :below rows + :do (loop :with previous-attribute := nil + :and string := "" + :for col :from 0 :below cols + :do (ffi::terminal-query-cell viscus col row) + (let ((char (get-cell-character viscus)) + (attribute (get-cell-attribute viscus))) + (when char + (unless (attribute-equal attribute previous-attribute) + (insert-string point string :attribute previous-attribute) + (setf previous-attribute attribute) + (setf string "")) + (setf string + (concatenate 'string + string + (string (if (eql char #\Nul) #\Space char)))))) + :finally (insert-string point string :attribute previous-attribute)) + (insert-character point #\newline))) + (move-to-line point (1+ (ffi::terminal-cursor-row viscus))) + (move-to-column point (ffi::terminal-cursor-col viscus)))) + +(defmethod adjust-point ((terminal terminal)) + (let* ((buffer (terminal-buffer terminal)) + (point (buffer-point buffer)) + (viscus (terminal-viscus terminal))) + (move-to-line point (1+ (ffi::terminal-cursor-row viscus))) + (move-to-column point (ffi::terminal-cursor-col viscus)))) + +(defmethod process-input ((terminal terminal)) + (ffi::terminal-process-input-nonblock (terminal-viscus terminal))) + +(defmethod input-character ((terminal terminal) character &key (mod 0)) + (ffi::terminal-input-char (terminal-viscus terminal) + (char-code character) + mod)) + +(defmethod input-key ((terminal terminal) key &key (mod 0)) + (ffi::terminal-input-key (terminal-viscus terminal) + key + mod)) + +(defun same-size-p (terminal rows cols) + (and (= (terminal-rows terminal) rows) + (= (terminal-cols terminal) cols))) + +(defmethod resize ((terminal terminal) + &key (rows (alexandria:required-argument :rows)) + (cols (alexandria:required-argument :cols))) + (unless (same-size-p terminal rows cols) + (setf (terminal-rows terminal) rows + (terminal-cols terminal) cols) + (ffi::terminal-resize (terminal-viscus terminal) rows cols))) + +;;; callbacks +(defun cb-damage (rect id) + (declare (ignore rect id))) + +(defun cb-moverect (dest src id) + (declare (ignore dest src id))) + +(defun cb-movecursor (pos oldpos visible id) + (declare (ignore pos oldpos visible id))) + +(defun cb-settermprop (prop val id) + (declare (ignore prop val id))) + +(defun cb-bell (id) + (declare (ignore id))) + +(defun cb-resize (rows cols id) + (let ((terminal (find-terminal-by-id id))) + (setf (terminal-rows terminal) rows + (terminal-cols terminal) cols))) + +(defun cb-sb-pushline (cols cells id) + (declare (ignore cols cells id))) + +(defun cb-sb-popline (cols cells id) + (declare (ignore cols cells id))) + +(ffi::set-callbacks :damage 'cb-damage + :moverect 'cb-moverect + :movecursor 'cb-movecursor + :settermprop 'cb-settermprop + :bell 'cb-bell + :resize 'cb-resize + :sb-pushline 'cb-sb-pushline + :sb-popline 'cb-sb-popline) diff --git a/extensions/terraform-mode/example.tf b/extensions/terraform-mode/example.tf new file mode 100644 index 000000000..e2943c8e2 --- /dev/null +++ b/extensions/terraform-mode/example.tf @@ -0,0 +1,3190 @@ +# SYNTAX TEST "Terraform.sublime-syntax" + +///////////////////////////////////////////////////////////////////// +// INLINE COMMENTS +///////////////////////////////////////////////////////////////////// + +///// +// Start of line inline comments with `#`. +///// + +# inline comment +# ^ source.terraform comment.line.terraform + +///// +// Start of line inline comments with `//`. +///// + +// foo +# ^ source.terraform comment.line.terraform + +///// +// Matches at random place in line + punctuation for `#`. +///// + + # bar +# ^ -comment -punctuation +# ^ punctuation.definition.comment.terraform +# ^^^^^ comment.line.terraform + +///// +// Matches at random place in line + punctuation for `//`. +///// + + // baz # blah +# ^ -comment -punctuation +# ^^ punctuation.definition.comment.terraform +# ^^^^^^^^^^^^^ comment.line.terraform + +///////////////////////////////////////////////////////////////////// +// BLOCK COMMENTS +///////////////////////////////////////////////////////////////////// + +///// +// Matches for a single line. +///// + + /* foo */ +# ^ -comment -punctuation +# ^^ punctuation.definition.comment.terraform +# ^^^^^^^^ comment.block.terraform +# ^^ punctuation.definition.comment.terraform + +///// +// Matches over multiple lines. +///// + + /* +# ^ -comment -punctuation +# ^^ punctuation.definition.comment.terraform +# ^^^^ comment.block.terraform + + foo +# ^^^^ comment.block.terraform + + */ +# ^^^^ comment.block.terraform +# ^^ punctuation.definition.comment.terraform + +///// +// Matches inline comments after block ends. +///// + + /* comment */ // inline +# ^ -comment -punctuation +# ^^ punctuation.definition.comment.terraform +# ^^^^^^^^^^^^ comment.block.terraform +# ^^ punctuation.definition.comment.terraform +# ^^^ -comment -punctuation +# ^^ punctuation.definition.comment.terraform +# ^^^^^^^^ comment.line.terraform + +///////////////////////////////////////////////////////////////////// +// LANGUAGE CONSTANTS +///////////////////////////////////////////////////////////////////// + +///// +// Matches `true`. +///// + + true +# ^ -constant +# ^^^^ constant.language.terraform +# ^ -constant + +///// +// Matches `false`. +///// + + false +# ^ -constant +# ^^^^^ constant.language.terraform +# ^ -constant + + +///// +// Matches `null`. +///// + + null +# ^ -constant +# ^^^^ constant.language.terraform +# ^ -constant + +///////////////////////////////////////////////////////////////////// +// INTEGER CONSTANTS +///////////////////////////////////////////////////////////////////// + +///// +// Matches integers. +///// + + 444 +# ^ -constant -numeric +# ^^^ constant.numeric.integer.terraform + +///// +// Matches zero. +///// + + 0 +# ^ -constant -numeric +# ^ constant.numeric.integer.terraform + +///// +// Matches one. +///// + + 1 +# ^ -constant -numeric +# ^ constant.numeric.integer.terraform + +///// +// Matches large integers. +///// + + 26345645634561 +# ^ -constant -numeric +# ^^^^^^^^^^^^^^ constant.numeric.integer.terraform + +///// +// Ignores integers inside identifiers. +///// + + aws_route53_zone.main.zone_id +# ^^^^^^^^^^^^^^^^ -numeric -constant +# ^ punctuation.accessor.dot.terraform +# ^^^^ variable.other.member.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^ variable.other.member.terraform + +///////////////////////////////////////////////////////////////////// +// FLOATING-POINT CONSTANTS +///////////////////////////////////////////////////////////////////// + +///// +// Matches floating-point numbers. +///// + + 1.2 +# ^ -constant -numeric +# ^ constant.numeric.float.terraform +# ^ punctuation.separator.decimal.terraform +# ^ constant.numeric.float.terraform + +///// +// Matches large floating-point numbers. +///// + + 61.28888888888 +# ^ -constant -numeric +# ^^ constant.numeric.float.terraform +# ^ punctuation.separator.decimal.terraform +# ^^^^^^^^^^^ constant.numeric.float.terraform + +///// +// Matches integers with exponents. +///// + + 1e12 +# ^ -constant -numeric +# ^ constant.numeric.float.terraform +# ^ punctuation.separator.exponent.terraform +# ^^ constant.numeric.float.terraform + +///// +// Matches floats with exponents. +///// + + 1.4E12 +# ^ -constant -numeric +# ^ constant.numeric.float.terraform +# ^ punctuation.separator.decimal.terraform +# ^ constant.numeric.float.terraform +# ^ punctuation.separator.exponent.terraform +# ^^ constant.numeric.float.terraform + +///// +// Matches floats with postive signed exponents. +///// + + 1.4e+12 +# ^ -constant -numeric +# ^ constant.numeric.float.terraform +# ^ punctuation.separator.decimal.terraform +# ^ constant.numeric.float.terraform +# ^^ punctuation.separator.exponent.terraform +# ^^ constant.numeric.float.terraform + +///// +// Matches floats with negative signed exponents. +///// + + 1.4E-12 +# ^ -constant -numeric +# ^ constant.numeric.float.terraform +# ^ punctuation.separator.decimal.terraform +# ^ constant.numeric.float.terraform +# ^^ punctuation.separator.exponent.terraform +# ^^ constant.numeric.float.terraform + +///////////////////////////////////////////////////////////////////// +// STRINGS +///////////////////////////////////////////////////////////////////// + +///// +// Matches punctuation and assigns meta_scope. +///// + + "a string" +# ^ -punctuation -string +# ^ punctuation.definition.string.begin.terraform +# ^^^^^^^^^^ string.quoted.double.terraform +# ^ punctuation.definition.string.end.terraform +# ^ -punctuation -string + +///// +// Matches character escapes. +///// + + "a \n b \r c \t d \" e \udead f \udeadbeef" +# ^ -punctuation -string +# ^ punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ string.quoted.double.terraform +# ^^ constant.character.escape.terraform +# ^^ constant.character.escape.terraform +# ^^ constant.character.escape.terraform +# ^^ constant.character.escape.terraform +# ^^^^^ constant.character.escape.terraform +# ^^^^^^^^^^ constant.character.escape.terraform +# ^ punctuation.definition.string.end.terraform + +///////////////////////////////////////////////////////////////////// +// Identifiers +///////////////////////////////////////////////////////////////////// + + this_is_an_identifier +# ^^^^^^^^^^^^^^^^^^^^^ variable.other.readwrite.terraform + + identifier.member_access +# ^^^^^^^^^^ variable.other.readwrite.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^^^^^^^ variable.other.member.terraform + + identifier-with-hyphens +# ^^^^^^^^^^^^^^^^^^^^^^^ variable.other.readwrite.terraform + + __EOF__ +# ^^^^^^^ variable.other.readwrite.terraform + + identifier. +// comment +# <- comment + +///////////////////////////////////////////////////////////////////// +// STRING INTERPOLATION +///////////////////////////////////////////////////////////////////// + +///// +// Correct scopes during interpolation. +///// + + "some ${interpolation} string" +# ^ -punctuation -string +# ^ punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.string.terraform +# ^^^^^ string.quoted.double.terraform +# ^^ meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^^^^^^^^^^^^^^ meta.interpolation.terraform +# ^^^^^^^^^^^^ source.terraform +# ^ meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^^^^^^^ string.quoted.double.terraform +# ^ punctuation.definition.string.end.terraform +# ^ -punctuation -string + +///// +// Matches left-trim and right-trim. +///// + + "%{~ fff ~}" +# ^^^^^^^^^^^^ meta.string.terraform +# ^^^^^^^^^^ meta.interpolation.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^ meta.interpolation.terraform keyword.operator.template.trim.left.terraform +# ^^^^^ source.terraform +# ^ keyword.operator.template.trim.right.terraform +# ^ punctuation.section.interpolation.end.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.end.terraform + +///// +// Matches operators +///// + + "${ something ? true : false }" +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ punctuation.section.interpolation.begin.terraform +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.interpolation.terraform +# ^^^^^^^^^^^^^^^^^^^^^^^^^^ source.terraform +# ^ keyword.operator.terraform +# ^^^^ meta.interpolation.terraform constant.language.terraform +# ^ meta.interpolation.terraform keyword.operator.terraform +# ^^^^^ meta.interpolation.terraform constant.language.terraform +# ^ meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.end.terraform + +///// +// Dot-access attributes in string interpolation +///// + + "hello ${aws_instance.ubuntu}" +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^ string.quoted.double.terraform +# ^^ meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^^^^^^^^^^^^^^^^^^^^ meta.interpolation.terraform +# ^ meta.interpolation.terraform punctuation.accessor.dot.terraform +# ^^^^^^ meta.interpolation.terraform variable.other.member.terraform +# ^ meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.end.terraform + +///// +// Handles function calls +///// + + "${formatdate("DD MMM YYYY hh:mm ZZZ", "2018-01-02T23:12:01Z")}" +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^^^^^^^^^^ meta.interpolation.terraform meta.function-call.terraform support.function.builtin.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^^^^^^^^^ meta.interpolation.terraform meta.function-call.terraform string.quoted.double.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform punctuation.separator.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^^^^^^^^ meta.interpolation.terraform meta.function-call.terraform string.quoted.double.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.end.terraform + +///// +// Handles nested function calls. +///// + + id = "db-final-snapshot-${md5(timestamp())}" +# ^^ variable.declaration.terraform variable.other.readwrite.terraform +# ^ keyword.operator.assignment.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^^^^^ string.quoted.double.terraform +# ^^ meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^^^ meta.interpolation.terraform meta.function-call.terraform support.function.builtin.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^^^^^^^^ meta.interpolation.terraform meta.function-call.terraform meta.function-call.terraform support.function.builtin.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.end.terraform + +///// +// Includes objects. +//// + + "something ${{test = 3}}" +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^ string.quoted.double.terraform +# ^^ meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^ meta.interpolation.terraform meta.braces.terraform punctuation.section.braces.begin.terraform +# ^^^^ meta.interpolation.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.interpolation.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.interpolation.terraform meta.braces.terraform constant.numeric.integer.terraform +# ^ meta.interpolation.terraform meta.braces.terraform punctuation.section.braces.end.terraform +# ^ meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.end.terraform + +///// +// Includes tuples. +//// + + "something ${[1, 2, 3]}" +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^ string.quoted.double.terraform +# ^^ meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^ meta.interpolation.terraform punctuation.section.brackets.begin.terraform +# ^ meta.interpolation.terraform constant.numeric.integer.terraform +# ^ meta.interpolation.terraform punctuation.separator.terraform +# ^ meta.interpolation.terraform constant.numeric.integer.terraform +# ^ meta.interpolation.terraform punctuation.separator.terraform +# ^ meta.interpolation.terraform constant.numeric.integer.terraform +# ^ meta.interpolation.terraform punctuation.section.brackets.end.terraform +# ^ meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.end.terraform + +///// +// Includes named values. +///// + + "${var.something}" +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^^^ meta.interpolation.terraform variable.language.terraform +# ^ meta.interpolation.terraform punctuation.accessor.dot.terraform +# ^^^^^^^^^ meta.interpolation.terraform variable.other.member.terraform +# ^ meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.end.terraform + +///// +// Handles regexes (various punctuation). +///// + + records = ["${replace("hostname.domain.com:1234", "/(.*):[0-9]{0,26}/", "$1")}"] +# ^^^^^^^ variable.declaration.terraform variable.other.readwrite.terraform +# ^ keyword.operator.assignment.terraform +# ^ punctuation.section.brackets.begin.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^^^^^^^ meta.interpolation.terraform meta.function-call.terraform support.function.builtin.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^^^^^^^^^^^ meta.interpolation.terraform meta.function-call.terraform string.quoted.double.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.end.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform punctuation.separator.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^^^^^ meta.interpolation.terraform meta.function-call.terraform string.quoted.double.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.end.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform punctuation.separator.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.interpolation.terraform meta.function-call.terraform string.quoted.double.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.end.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.end.terraform +# ^ punctuation.section.brackets.end.terraform +# ^ -meta -string -variable -punctuation + +///// +// Handles nested interpolation. +///// + + "${file("${path.module}/text_files/ecs_app")}" +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^^^^ meta.interpolation.terraform meta.function-call.terraform support.function.builtin.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.interpolation.terraform meta.function-call.terraform meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^^^^ meta.interpolation.terraform meta.function-call.terraform meta.interpolation.terraform variable.language.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform meta.interpolation.terraform punctuation.accessor.dot.terraform +# ^^^^^^ meta.interpolation.terraform meta.function-call.terraform meta.interpolation.terraform variable.other.member.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^^^^^^^^^^^^^^^^^^^ meta.interpolation.terraform meta.function-call.terraform string.quoted.double.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.end.terraform +# ^ meta.interpolation.terraform meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.end.terraform + +///////////////////////////////////////////////////////////////////// +// Template If Directives +///////////////////////////////////////////////////////////////////// + +///// +// Matches if/endif directives. +///// + + "${ if name == "Mary" }${name}${ endif ~}" +# ^ -string -punctuation +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.interpolation.terraform +# ^^ meta.interpolation.terraform keyword.control.terraform +# ^^ meta.interpolation.terraform keyword.operator.terraform +# ^ source.terraform meta.interpolation.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^ source.terraform meta.interpolation.terraform string.quoted.double.terraform +# ^ source.terraform meta.interpolation.terraform string.quoted.double.terraform punctuation.definition.string.end.terraform +# ^ punctuation.section.interpolation.end.terraform +# ^^ meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^ punctuation.section.interpolation.end.terraform +# ^^ meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^^^^^ meta.interpolation.terraform keyword.control.terraform +# ^ meta.interpolation.terraform keyword.operator.template.trim.right.terraform +# ^ meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.end.terraform +# ^ -string -punctuation + +///// +// Matches if/else/endif directives. +///// + + "%{ if name == "Mary" }${name}%{ else }${ "Mary" }%{ endif ~}" +# ^ -string -punctuation +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.interpolation.terraform +# ^^ meta.interpolation.terraform keyword.control.terraform +# ^^ meta.interpolation.terraform keyword.operator.terraform +# ^ source.terraform meta.interpolation.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^ source.terraform meta.interpolation.terraform string.quoted.double.terraform +# ^ source.terraform meta.interpolation.terraform string.quoted.double.terraform punctuation.definition.string.end.terraform +# ^ punctuation.section.interpolation.end.terraform +# ^^ meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^ punctuation.section.interpolation.end.terraform +# ^^ meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^^^^ meta.interpolation.terraform keyword.control.terraform +# ^ meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^^ meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^ meta.interpolation.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^ meta.interpolation.terraform string.quoted.double.terraform +# ^ meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^ punctuation.section.interpolation.end.terraform +# ^^ meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^^^^^ meta.interpolation.terraform keyword.control.terraform +# ^ meta.interpolation.terraform keyword.operator.template.trim.right.terraform +# ^ meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.end.terraform +# ^ -string -punctuation + +///// +// Matches for/in/endfor directives. +///// + + "%{ for name in var.names ~}${name}%{ endfor ~}" +# ^ -string -punctuation +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^^^ keyword.control.terraform +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.interpolation.terraform +# ^^ keyword.control.terraform +# ^^^ meta.interpolation.terraform variable.language.terraform +# ^ meta.interpolation.terraform punctuation.accessor.dot.terraform +# ^^^^^ meta.interpolation.terraform variable.other.member.terraform +# ^ keyword.operator.template.trim.right.terraform +# ^ meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^^ meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^ meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^^ meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^^^^^^ meta.interpolation.terraform keyword.control.terraform +# ^ meta.interpolation.terraform keyword.operator.template.trim.right.terraform +# ^ meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.end.terraform +# ^ -string -punctuation + +///////////////////////////////////////////////////////////////////// +// Operators +///////////////////////////////////////////////////////////////////// + +///// +// Comparison +///// + + a == b +# ^ -keyword -operator +# ^^ keyword.operator.terraform +# ^ -keyword -operator + + a != b +# ^ -keyword -operator +# ^^ keyword.operator.terraform +# ^ -keyword -operator + + a < b +# ^ -keyword -operator +# ^ keyword.operator.terraform +# ^ -keyword -operator + + a <= b +# ^ -keyword -operator +# ^^ keyword.operator.terraform +# ^ -keyword -operator + + a > b +# ^ -keyword -operator +# ^ keyword.operator.terraform +# ^ -keyword -operator + + a >= b +# ^ -keyword -operator +# ^^ keyword.operator.terraform +# ^ -keyword -operator + +///// +// Arithmetic +///// + + a + b +# ^ -keyword -operator +# ^ keyword.operator.arithmetic.terraform +# ^ -keyword -operator + + a - b +# ^ -keyword -operator +# ^ keyword.operator.arithmetic.terraform +# ^ -keyword -operator + + a * b +# ^ -keyword -operator +# ^ keyword.operator.arithmetic.terraform +# ^ -keyword -operator + + a / b +# ^ -keyword -operator +# ^ keyword.operator.arithmetic.terraform +# ^ -keyword -operator + + a % b +# ^ -keyword -operator +# ^ keyword.operator.arithmetic.terraform +# ^ -keyword -operator + + -a +# ^ -keyword -operator +# ^ keyword.operator.arithmetic.terraform +# ^ -keyword -operator + +///// +// Logic +///// + + a && b +# ^^ -keyword -operator +# ^^ keyword.operator.logical.terraform +# ^^ -keyword -operator + + a || b +# ^^ -keyword -operator +# ^^ keyword.operator.logical.terraform +# ^^ -keyword -operator + + !a +# ^^ -keyword -operator +# ^ keyword.operator.logical.terraform +# ^^ -keyword -operator + +///// +// Conditional +///// + + length(some_list) > 0 ? some_list[0] : default +# ^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^^^^^^^^ meta.function-call.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ keyword.operator.terraform +# ^ constant.numeric.integer.terraform +# ^ keyword.operator.terraform +# ^ punctuation.section.brackets.begin.terraform +# ^ constant.numeric.integer.terraform +# ^ punctuation.section.brackets.end.terraform +# ^ keyword.operator.terraform + +///// +// Ellipsis +///// + + hhh([55, 2453, 2]...) +# ^^^ meta.function-call.terraform variable.function.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ punctuation.section.brackets.begin.terraform +# ^^ constant.numeric.integer.terraform +# ^ punctuation.separator.terraform +# ^^^^ constant.numeric.integer.terraform +# ^ punctuation.separator.terraform +# ^ constant.numeric.integer.terraform +# ^ punctuation.section.brackets.end.terraform +# ^^^ keyword.operator.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + +///////////////////////////////////////////////////////////////////// +// Brackets: Index Operations and Arrays +///////////////////////////////////////////////////////////////////// + +///// +// Index Operations +///// + + thing[1] +# ^ -punctuation +# ^ punctuation.section.brackets.begin.terraform +# ^ constant.numeric.integer.terraform +# ^ punctuation.section.brackets.end.terraform +# ^ -punctuation + +///// +// Arrays of literals +///// + + ["a", "b", "c"] +# ^ punctuation.section.brackets.begin.terraform +# ^ punctuation.definition.string.begin.terraform +# ^^^ string.quoted.double.terraform +# ^ punctuation.definition.string.end.terraform +# ^ punctuation.separator.terraform +# ^ punctuation.definition.string.begin.terraform +# ^^^ string.quoted.double.terraform +# ^ punctuation.definition.string.end.terraform +# ^ punctuation.separator.terraform +# ^ punctuation.definition.string.begin.terraform +# ^^^ string.quoted.double.terraform +# ^ punctuation.definition.string.end.terraform +# ^ punctuation.section.brackets.end.terraform + +///// +// Allows inline comments +///// + + [1, /* inline */ 2] +# ^ punctuation.section.brackets.begin.terraform +# ^ constant.numeric.integer.terraform +# ^ punctuation.separator.terraform +# ^^ punctuation.definition.comment.terraform +# ^^^^^^^^^^^^ comment.block.terraform +# ^^ punctuation.definition.comment.terraform +# ^ constant.numeric.integer.terraform +# ^ punctuation.section.brackets.end.terraform + +///// +// Allows expression over multiple lines +///// + + [ +# ^ punctuation.section.brackets.begin.terraform + 1, +# ^ constant.numeric.integer.terraform +# ^ punctuation.separator.terraform + 2 +# ^ constant.numeric.integer.terraform + ] +# ^ punctuation.section.brackets.end.terraform + +///// +// Allows operators +///// + + [ 1 + 2 ] +# ^ punctuation.section.brackets.begin.terraform +# ^ constant.numeric.integer.terraform +# ^ keyword.operator.arithmetic.terraform +# ^ constant.numeric.integer.terraform +# ^ punctuation.section.brackets.end.terraform + +///// +// Splat operator +///// + + tuple[*].foo.bar[0] +# ^ punctuation.section.brackets.begin.terraform +# ^ punctuation.section.brackets.end.terraform keyword.operator.splat.terraform +# ^ punctuation.section.brackets.end.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^ variable.other.member.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^ variable.other.member.terraform +# ^ punctuation.section.brackets.begin.terraform +# ^ constant.numeric.integer.terraform +# ^ punctuation.section.brackets.end.terraform + +///// +// Handle nested arrays +///// + + counts = [ +# ^^^^^^ variable.declaration.terraform variable.other.readwrite.terraform +# ^ keyword.operator.assignment.terraform +# ^ punctuation.section.brackets.begin.terraform + [ 1, 2], +# ^ punctuation.section.brackets.begin.terraform +# ^ constant.numeric.integer.terraform +# ^ punctuation.separator.terraform +# ^ constant.numeric.integer.terraform +# ^ punctuation.section.brackets.end.terraform +# ^ punctuation.separator.terraform + [ 6, 7] +# ^ punctuation.section.brackets.begin.terraform +# ^ constant.numeric.integer.terraform +# ^ punctuation.separator.terraform +# ^ constant.numeric.integer.terraform +# ^ punctuation.section.brackets.end.terraform + ] +# ^ punctuation.section.brackets.end.terraform +# ^ -punctuation + +///// +// Attribute-access inside arrays +///// + + [ aws_instance.ubuntu, aws_instance.freebsd ] +# ^ punctuation.section.brackets.begin.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^ variable.other.member.terraform +# ^ punctuation.separator.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^ variable.other.member.terraform +# ^ punctuation.section.brackets.end.terraform + +///// +// Includes functions. +///// + + [ upper("ggh") ] +# ^ punctuation.section.brackets.begin.terraform +# ^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ punctuation.section.brackets.end.terraform + +///// +// Include objects. +///// + + [{a = 1}, {g = 2}] +# ^ punctuation.section.brackets.begin.terraform +# ^ meta.braces.terraform punctuation.section.braces.begin.terraform +# ^ meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.braces.terraform constant.numeric.integer.terraform +# ^ meta.braces.terraform punctuation.section.braces.end.terraform +# ^ punctuation.separator.terraform +# ^ meta.braces.terraform punctuation.section.braces.begin.terraform +# ^ meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.braces.terraform constant.numeric.integer.terraform +# ^ meta.braces.terraform punctuation.section.braces.end.terraform +# ^ punctuation.section.brackets.end.terraform + +///// +// Includes named values +///// + + [local.thing1, local.thing2] +# ^ punctuation.section.brackets.begin.terraform +# ^^^^^ variable.language.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^ variable.other.member.terraform +# ^ punctuation.separator.terraform +# ^^^^^ variable.language.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^ variable.other.member.terraform +# ^ punctuation.section.brackets.end.terraform + +///////////////////////////////////////////////////////////////////// +// Collections: Objects +///////////////////////////////////////////////////////////////////// + +///// +// Key/value pairs separated by newlines. +///// + + { +# ^ meta.braces.terraform punctuation.section.braces.begin.terraform + name = "John" +# ^^^^ meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.braces.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^ meta.braces.terraform string.quoted.double.terraform + age = 52 +# ^^^ meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.braces.terraform keyword.operator.assignment.terraform +# ^^ meta.braces.terraform constant.numeric.integer.terraform + } +# ^ meta.braces.terraform punctuation.section.braces.end.terraform + +///// +// Key/value pairs separated by commas. +///// + + {name = "John", age = 52} +# ^ meta.braces.terraform punctuation.section.braces.begin.terraform +# ^^^^ meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.braces.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^ meta.braces.terraform string.quoted.double.terraform +# ^ meta.braces.terraform punctuation.separator.terraform +# ^^^ meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.braces.terraform punctuation.section.braces.end.terraform + +///// +// Allows operators in key values. +///// + + { name = 1 + 1 } +# ^ meta.braces.terraform punctuation.section.braces.begin.terraform +# ^^^^ meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.braces.terraform constant.numeric.integer.terraform +# ^ meta.braces.terraform keyword.operator.arithmetic.terraform +# ^ meta.braces.terraform constant.numeric.integer.terraform +# ^ meta.braces.terraform punctuation.section.braces.end.terraform +# ^ -meta + +///// +// Allows tuples as key values. +///// + + { list = [ 1, 2, 3 ]} +# ^ meta.braces.terraform punctuation.section.braces.begin.terraform +# ^^^^ meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.braces.terraform punctuation.section.brackets.begin.terraform +# ^ meta.braces.terraform constant.numeric.integer.terraform +# ^ meta.braces.terraform punctuation.separator.terraform +# ^ meta.braces.terraform constant.numeric.integer.terraform +# ^ meta.braces.terraform punctuation.separator.terraform +# ^ meta.braces.terraform constant.numeric.integer.terraform +# ^ meta.braces.terraform punctuation.section.brackets.end.terraform +# ^ meta.braces.terraform punctuation.section.braces.end.terraform +# ^ -meta + +///// +// Allows function calls as values. +///// + + { +# ^ meta.braces.terraform punctuation.section.braces.begin.terraform + a = upper("l"), +# ^ meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.braces.terraform keyword.operator.assignment.terraform +# ^^^^^ meta.braces.terraform meta.function-call.terraform support.function.builtin.terraform +# ^ meta.braces.terraform meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.braces.terraform meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.braces.terraform meta.function-call.terraform string.quoted.double.terraform +# ^ meta.braces.terraform meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ meta.braces.terraform punctuation.separator.terraform + } +# ^ meta.braces.terraform punctuation.section.braces.end.terraform + +///// +// Allows nested collection literals. +///// + + { +# ^ meta.braces.terraform punctuation.section.braces.begin.terraform + obj1 = { +# ^^^^ meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.braces.terraform meta.braces.terraform punctuation.section.braces.begin.terraform + obj2 = { +# ^^^^ meta.braces.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.braces.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.braces.terraform meta.braces.terraform meta.braces.terraform punctuation.section.braces.begin.terraform + value = 5 +# ^^^^^ meta.braces.terraform meta.braces.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.braces.terraform meta.braces.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.braces.terraform meta.braces.terraform meta.braces.terraform constant.numeric.integer.terraform + } +# ^ meta.braces.terraform meta.braces.terraform meta.braces.terraform punctuation.section.braces.end.terraform + } +# ^ meta.braces.terraform meta.braces.terraform punctuation.section.braces.end.terraform + } +# ^ meta.braces.terraform punctuation.section.braces.end.terraform +# ^ -meta + +///// +// Allows attribute-access as rvalue, including named values. +///// + + { lvalue = var.rvalue } +# ^ meta.braces.terraform punctuation.section.braces.begin.terraform +# ^^^^^^ meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.braces.terraform keyword.operator.assignment.terraform +# ^^^ meta.braces.terraform variable.language.terraform +# ^ meta.braces.terraform punctuation.accessor.dot.terraform +# ^^^^^^ meta.braces.terraform variable.other.member.terraform +# ^ meta.braces.terraform punctuation.section.braces.end.terraform +# ^ -meta + +///// +// Allows strings as keys. +///// + + {"gggg" = "gggg"} +# ^ meta.braces.terraform punctuation.section.braces.begin.terraform +# ^ meta.braces.terraform meta.mapping.key.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^ meta.braces.terraform meta.mapping.key.terraform string.quoted.double.terraform +# ^ meta.braces.terraform meta.mapping.key.terraform string.quoted.double.terraform punctuation.definition.string.end.terraform +# ^ meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.braces.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^ meta.braces.terraform string.quoted.double.terraform +# ^ meta.braces.terraform string.quoted.double.terraform punctuation.definition.string.end.terraform +# ^ meta.braces.terraform punctuation.section.braces.end.terraform + +///// +// Allows expressions + operators as keys. +///// + + {(1 + 2) = "gggg"} +# ^ meta.braces.terraform punctuation.section.braces.begin.terraform +# ^ meta.braces.terraform meta.mapping.key.terraform punctuation.section.parens.begin.terraform +# ^ meta.braces.terraform meta.mapping.key.terraform constant.numeric.integer.terraform +# ^ meta.braces.terraform meta.mapping.key.terraform keyword.operator.arithmetic.terraform +# ^ meta.braces.terraform meta.mapping.key.terraform constant.numeric.integer.terraform +# ^ meta.braces.terraform meta.mapping.key.terraform punctuation.section.parens.end.terraform +# ^ meta.braces.terraform keyword.operator.terraform +# ^ meta.braces.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^ meta.braces.terraform string.quoted.double.terraform +# ^ meta.braces.terraform punctuation.section.braces.end.terraform + +///// +// Allows function calls as keys. +///// + + {(func()) = 1} +# ^ meta.braces.terraform punctuation.section.braces.begin.terraform +# ^ meta.braces.terraform meta.mapping.key.terraform punctuation.section.parens.begin.terraform +# ^^^^ meta.braces.terraform meta.mapping.key.terraform meta.function-call.terraform variable.function.terraform +# ^ meta.braces.terraform meta.mapping.key.terraform meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.braces.terraform meta.mapping.key.terraform meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ meta.braces.terraform meta.mapping.key.terraform punctuation.section.parens.end.terraform +# ^ meta.braces.terraform keyword.operator.terraform +# ^ meta.braces.terraform constant.numeric.integer.terraform +# ^ meta.braces.terraform punctuation.section.braces.end.terraform + +///// +// Allows attribute-access as keys. +//// + + {(var.path) = 1} +# ^ meta.braces.terraform punctuation.section.braces.begin.terraform +# ^ meta.braces.terraform meta.mapping.key.terraform punctuation.section.parens.begin.terraform +# ^^^ meta.braces.terraform meta.mapping.key.terraform variable.language.terraform +# ^ meta.braces.terraform meta.mapping.key.terraform punctuation.accessor.dot.terraform +# ^^^^ meta.braces.terraform meta.mapping.key.terraform variable.other.member.terraform +# ^ meta.braces.terraform meta.mapping.key.terraform punctuation.section.parens.end.terraform +# ^ meta.braces.terraform keyword.operator.terraform +# ^ meta.braces.terraform constant.numeric.integer.terraform +# ^ meta.braces.terraform punctuation.section.braces.end.terraform + +///////////////////////////////////////////////////////////////////// +// Attribute Access +///////////////////////////////////////////////////////////////////// + +///// +// Matches dot-access +///// + + aws_instance.ubuntu[*].private_dns +# ^ punctuation.accessor.dot.terraform +# ^^^^^^ variable.other.member.terraform +# ^ punctuation.section.brackets.begin.terraform +# ^ punctuation.section.brackets.end.terraform keyword.operator.splat.terraform +# ^ punctuation.section.brackets.end.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^^^^^ variable.other.member.terraform +# ^ -variable -punctuation + +///// +// Ignores dot-access in string literals +///// + + "aws_instance.ubuntu" +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^ -variable +# ^^^^^^^^^^^^^^^^^^^^^ string.quoted.double.terraform + +///// +// Matches inside for-expressions +///// + + [for l in var.letters: upper(l)] +# ^ punctuation.section.brackets.begin.terraform +# ^^^ keyword.control.loop.for.terraform +# ^ variable.other.readwrite.terraform +# ^^ keyword.control.loop.in.terraform +# ^^^ variable.language.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^ variable.other.member.terraform +# ^ punctuation.section.block.loop.for.terraform +# ^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ punctuation.section.brackets.end.terraform + +///// +// Attribute-only splat +///// + + tuple.*.foo.bar[0] +# ^ punctuation.accessor.dot.terraform +# ^ keyword.operator.splat.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^ variable.other.member.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^ variable.other.member.terraform +# ^ punctuation.section.brackets.begin.terraform +# ^ constant.numeric.integer.terraform +# ^ punctuation.section.brackets.end.terraform + +///// +// Matches subscript indexes +///// + + aws_route53_zone.project.name_servers.1 +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^ variable.other.member.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^^^^^^ variable.other.member.terraform +# ^ punctuation.accessor.dot.terraform +# ^ constant.numeric.integer.terraform +# ^ -constant -punctuation -variable + +///////////////////////////////////////////////////////////////////// +// Attribute Definition +///////////////////////////////////////////////////////////////////// + +///// +// Basic definition +///// + + attribute = "string" +# ^^^^^^^^^ variable.declaration.terraform variable.other.readwrite.terraform +# ^ keyword.operator.assignment.terraform +# ^^^^^^^^ meta.string.terraform string.quoted.double.terraform +# ^ punctuation.definition.string.begin.terraform +# ^ punctuation.definition.string.end.terraform + + provider = google-beta.impersonated +# ^^^^^^^^ variable.declaration.terraform variable.other.readwrite.terraform +# ^ keyword.operator.assignment.terraform +# ^^^^^^^^^^^ variable.other.readwrite.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^^^^^^ variable.other.member.terraform + + +///// +// Meta-arguments +//// + + for_each = toset([]) +# ^^^^^^^^ keyword.control.loop.for.terraform +# ^ keyword.operator.assignment.terraform +# ^^^^^^^^^ meta.function-call.terraform +# ^^^^^ support.function.builtin.terraform +# ^ punctuation.section.parens.begin.terraform +# ^ punctuation.section.brackets.begin.terraform +# ^ punctuation.section.brackets.end.terraform +# ^ punctuation.section.parens.end.terraform + + count = length(var.availability_zones) +# ^^^^^ variable.declaration.terraform keyword.control.conditional.terraform +# ^ keyword.operator.assignment.terraform +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.function-call.terraform +# ^^^^^^ support.function.builtin.terraform +# ^ punctuation.section.parens.begin.terraform +# ^^^ variable.language.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^^^^^^^^^^^^ variable.other.member.terraform +# ^ punctuation.section.parens.end.terraform + +///// +// Populate an attribute from a variable value +///// + + (foo) = "baz" +# ^ punctuation.section.parens.begin.terraform +# ^^^ variable.declaration.terraform variable.other.readwrite.terraform +# ^ punctuation.section.parens.end.terraform +# ^ keyword.operator.assignment.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^ string.quoted.double.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.end.terraform + +///////////////////////////////////////////////////////////////////// +// Function Calls +///////////////////////////////////////////////////////////////////// + +///// +// Basic call. +///// + + thing(l) +# ^^^^^ meta.function-call.terraform variable.function.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + +///// +// Matches parameters, attribute-access, literals, operators, commas. +///// + + cidrthingy(aws_vpc.main.cidr_block, 4, count.index+1) +# ^^^^^^^^^^ meta.function-call.terraform variable.function.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^^^^^^ meta.function-call.terraform +# ^ meta.function-call.terraform punctuation.accessor.dot.terraform +# ^^^^ meta.function-call.terraform variable.other.member.terraform +# ^ meta.function-call.terraform punctuation.accessor.dot.terraform +# ^^^^^^^^^^ meta.function-call.terraform variable.other.member.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^^^^^ meta.function-call.terraform +# ^ meta.function-call.terraform punctuation.accessor.dot.terraform +# ^^^^^ meta.function-call.terraform variable.other.member.terraform +# ^ meta.function-call.terraform keyword.operator.arithmetic.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ -meta -function-call -variable + +///// +// Matches arrays and splat as parameters. +///// + + y6y([55, 2453, 2]..., [55555555]) +# ^^^ meta.function-call.terraform variable.function.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^^^^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^^^ meta.function-call.terraform keyword.operator.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^^^^^^^^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + +///// +// Matches objects as parameters. +///// + + some({a = 1, b = "2"}) +# ^^^^ meta.function-call.terraform variable.function.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.braces.begin.terraform +# ^ meta.function-call.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.function-call.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.function-call.terraform meta.braces.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.function-call.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.braces.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + +///// +// Nested function calls. +///// + + func(thing(yep(1))) +# ^^^^ meta.function-call.terraform variable.function.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^^^^ meta.function-call.terraform meta.function-call.terraform variable.function.terraform +# ^ meta.function-call.terraform meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^^ meta.function-call.terraform meta.function-call.terraform meta.function-call.terraform variable.function.terraform +# ^ meta.function-call.terraform meta.function-call.terraform meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform meta.function-call.terraform meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform meta.function-call.terraform meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ meta.function-call.terraform meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ -function + +///// +// Parameters spanning multiple lines. +///// + + func( +# ^^^^ meta.function-call.terraform variable.function.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform + 1, +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform + 2 +# ^ meta.function-call.terraform constant.numeric.integer.terraform + ) +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + +///// +// Allow object for-expressions. +//// + + thing({for i, v in ["a"]: v => i...}) +# ^^^^^ meta.function-call.terraform variable.function.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.braces.begin.terraform +# ^^^ meta.function-call.terraform meta.braces.terraform keyword.control.loop.for.terraform +# ^ meta.function-call.terraform variable.other.readwrite.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform variable.other.readwrite.terraform +# ^^ meta.function-call.terraform keyword.control.loop.in.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.section.block.loop.for.terraform +# ^ meta.function-call.terraform variable.other.readwrite.terraform +# ^^ meta.function-call.terraform punctuation.separator.key-value.terraform +# ^ meta.function-call.terraform variable.other.readwrite.terraform +# ^^^ meta.function-call.terraform keyword.operator.terraform +# ^ meta.function-call.terraform punctuation.section.braces.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + +///// +// Allow tuple for-expressions. +///// + + func([for v in ["a", "b"]: v]) +# ^^^^ meta.function-call.terraform variable.function.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^^^ meta.function-call.terraform keyword.control.loop.for.terraform +# ^ meta.function-call.terraform variable.other.readwrite.terraform +# ^^ meta.function-call.terraform keyword.control.loop.in.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.section.block.loop.for.terraform +# ^ meta.function-call.terraform variable.other.readwrite.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + +///////////////////////////////////////////////////////////////////// +// Built-in Terraform Functions +// TODO: match % placeholders in format()-family first parameters +// TODO: match regexs in regex()-family first parameters +////////////////////////`///////////////////////////////////////////// + +///// +// Numeric Functions +///// + + abs(23) +# ^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + ceil(5.1) +# ^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^^ meta.function-call.terraform constant.numeric.float.terraform +# ^ meta.function-call.terraform constant.numeric.float.terraform punctuation.separator.decimal.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + floor(5) +# ^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + log(50, 10) +# ^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + max(12, 54, 3) +# ^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + min(12, 54, 3) +# ^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + pow(3, 2) +# ^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + signum(-13) +# ^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform keyword.operator.arithmetic.terraform +# ^^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + +///// +// String Functions +///// + + chomp("hello\n") +# ^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform constant.character.escape.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + format("Hello, %s!", "Ander") +# ^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.end.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + formatlist("Hello, %s!", ["Valentina", "Ander"]) +# ^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + indent(2, "[\n foo,\n bar,\n]\n") +# ^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + join(", ", ["foo", "bar", "baz"]) +# ^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + lower("HELLO") +# ^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + regex("[a-z]+", "53453453.345345aaabbbccc23454") +# ^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + regexall("[a-z]+", "1234abcd5678efgh9") +# ^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + replace("1 + 2 + 3", "+", "-") +# ^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + split(",", "foo") +# ^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + strrev("hello") +# ^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + substr("🤔🤷", 0, 1) +# ^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + title("hello world") +# ^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + trimspace(" hello\n\n") +# ^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + upper("hello") +# ^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + +///// +// Collection Functions +///// + + chunklist(["a", "b"], 2) +# ^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + coalesce("a", "b") +# ^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + coalescelist([], ["c", "d"]) +# ^^^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + compact(["a", "", "b"]) +# ^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.end.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + concat(["a"], ["c"]) +# ^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + contains(["a"], "a") +# ^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + distinct(["a", "b", "a"]) +# ^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + element(["a", "b"], 1) +# ^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + flatten([[["a", "b"]], ["c"]]) +# ^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + index(["a"], "b") +# ^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + keys({a=1, c=2}) +# ^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.braces.begin.terraform +# ^ meta.function-call.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.function-call.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.function-call.terraform meta.braces.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.function-call.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.function-call.terraform meta.braces.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.braces.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + length([]) +# ^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + list("a", "b", "c") +# ^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + lookup({a="ay", b="bee"}, "a", "what?") +# ^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.braces.begin.terraform +# ^ meta.function-call.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.function-call.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.function-call.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.braces.end.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + map("a", "b") +# ^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + matchkeys(["i-123", "i-abc"], ["us-west", "us-east"], ["us-east"]) +# ^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + merge({a="b"}, {e="f"}) +# ^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.braces.begin.terraform +# ^ meta.function-call.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.function-call.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.braces.end.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.braces.begin.terraform +# ^ meta.function-call.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.function-call.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.braces.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + range(1, 4) +# ^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + reverse([1, 2, 3]) +# ^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + setintersection(["a", "b"], ["b", "c"]) +# ^^^^^^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + setproduct(["development"], ["app1", "app2"]) +# ^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + setunion(["a"], ["b"], ["d"]) +# ^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + slice(["a", "b"], 1, 1) +# ^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + sort(["e", "d"]) +# ^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + transpose({a = ["1", "2"], b = ["2", "3"]}) +# ^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.braces.begin.terraform +# ^ meta.function-call.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.function-call.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.function-call.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.braces.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + values({a=3, c=2, d=1}) +# ^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.braces.begin.terraform +# ^ meta.function-call.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.function-call.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.function-call.terraform meta.braces.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.function-call.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.function-call.terraform meta.braces.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.function-call.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.function-call.terraform meta.braces.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.braces.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + zipmap(["a", "b"], [1, 2]) +# ^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + +///// +// Encoding Functions +///// + + base64decode("SGVsbG8gV29ybGQ=") +# ^^^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + base64encode("Hello World") +# ^^^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + base64gzip("Hello World") +# ^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + csvdecode("a,b,c\n1,2,3\n4,5,6") +# ^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + jsondecode("{\"hello\": \"world\"}") +# ^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + jsonencode({hello="world"}) +# ^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.braces.begin.terraform +# ^^^^^ meta.function-call.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.function-call.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.braces.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + urlencode("Hello World") +# ^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + yamldecode("true") +# ^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + yamlencode({a = "b", c = "d"}) +# ^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.braces.begin.terraform +# ^ meta.function-call.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.function-call.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.function-call.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + +///// +// Filesystem Functions +///// + + abspath(path.root) +# ^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^^^ meta.function-call.terraform variable.language.terraform +# ^ meta.function-call.terraform punctuation.accessor.dot.terraform +# ^^^^ meta.function-call.terraform variable.other.member.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + dirname("foo/bar/baz.txt") +# ^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + pathexpand("~/.ssh/id_rsa") +# ^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + basename("foo/bar/baz.txt") +# ^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + file("${path.module}/hello.txt") +# ^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^^^^ meta.function-call.terraform meta.interpolation.terraform variable.language.terraform +# ^ meta.function-call.terraform meta.interpolation.terraform punctuation.accessor.dot.terraform +# ^^^^^^ meta.function-call.terraform meta.interpolation.terraform variable.other.member.terraform +# ^ meta.function-call.terraform meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + fileexists("${path.module}/hello.txt") +# ^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^^^^ meta.function-call.terraform meta.interpolation.terraform variable.language.terraform +# ^ meta.function-call.terraform meta.interpolation.terraform punctuation.accessor.dot.terraform +# ^^^^^^ meta.function-call.terraform meta.interpolation.terraform variable.other.member.terraform +# ^ meta.function-call.terraform meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + fileset(path.module, "files/*.txt") +# ^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^^^ meta.function-call.terraform variable.language.terraform +# ^ meta.function-call.terraform punctuation.accessor.dot.terraform +# ^^^^^^ meta.function-call.terraform variable.other.member.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + filebase64("${path.module}/hello.txt") +# ^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^^^^ meta.function-call.terraform meta.interpolation.terraform variable.language.terraform +# ^ meta.function-call.terraform meta.interpolation.terraform punctuation.accessor.dot.terraform +# ^^^^^^ meta.function-call.terraform meta.interpolation.terraform variable.other.member.terraform +# ^ meta.function-call.terraform meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + templatefile("${path.module}/backends.tmpl", { +# ^^^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^^^^ meta.function-call.terraform meta.interpolation.terraform variable.language.terraform +# ^ meta.function-call.terraform meta.interpolation.terraform punctuation.accessor.dot.terraform +# ^^^^^^ meta.function-call.terraform meta.interpolation.terraform variable.other.member.terraform +# ^ meta.function-call.terraform meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^^^^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.braces.begin.terraform + port = 8080, +# ^^^^ meta.function-call.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.function-call.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^^^^ meta.function-call.terraform meta.braces.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform meta.braces.terraform + ip_addrs = ["10.0.0.1", "10.0.0.2"] +# ^^^^^^^^ meta.function-call.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.function-call.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^ meta.function-call.terraform meta.braces.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.brackets.end.terraform + }) +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.braces.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + +///// +// Date & Time Functions +///// + + formatdate("DD MMM YYYY hh:mm ZZZ", "2018-01-02T23:12:01Z") +# ^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + timeadd("2017-11-22T00:00:00Z", "10m") +# ^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + timestamp() +# ^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + +///// +// Hash & Crypto Functions +///// + + base64sha256("hello world") +# ^^^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + base64sha512("hello world") +# ^^^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + bcrypt("hello world") +# ^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + filebase64sha256(file("filename")) +# ^^^^^^^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^^^ meta.function-call.terraform meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^ meta.function-call.terraform meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ -function + + filebase64sha512(file("filename")) +# ^^^^^^^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^^^ meta.function-call.terraform meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^ meta.function-call.terraform meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ -function + + filemd5(file("filename")) +# ^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^^^ meta.function-call.terraform meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^ meta.function-call.terraform meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + filemd1(file("filename")) +# ^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^^^ meta.function-call.terraform meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^ meta.function-call.terraform meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ -function + + filesha256(file("filename")) +# ^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^^^ meta.function-call.terraform meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^ meta.function-call.terraform meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ -function + + filesha512(file("filename")) +# ^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^^^ meta.function-call.terraform meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^ meta.function-call.terraform meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ -function + + md5("hello world") +# ^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + rsadecrypt(filebase64("${path.module}/ciphertext"), file("privatekey.pem")) +# ^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^^^^^^^^^ meta.function-call.terraform meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform meta.function-call.terraform meta.interpolation.terraform punctuation.section.interpolation.begin.terraform +# ^^^^ meta.function-call.terraform meta.function-call.terraform meta.interpolation.terraform variable.language.terraform +# ^ meta.function-call.terraform meta.function-call.terraform meta.interpolation.terraform punctuation.accessor.dot.terraform +# ^^^^^^ meta.function-call.terraform meta.function-call.terraform meta.interpolation.terraform variable.other.member.terraform +# ^ meta.function-call.terraform meta.function-call.terraform meta.interpolation.terraform punctuation.section.interpolation.end.terraform +# ^^^^^^^^^^^^ meta.function-call.terraform meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^^^^ meta.function-call.terraform meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^^ meta.function-call.terraform meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ -function + + sha1("hello world") +# ^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + sha256("hello world") +# ^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + sha512("hello world") +# ^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + uuid() +# ^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + uuidv5("dns", "www.terraform.io") +# ^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + +///// +// IP Network Functions +///// + + cidrhost("10.12.127.0/20", 16) +# ^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + cidrnetmask("172.16.0.0/12") +# ^^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + cidrsubnet("172.16.0.0/12", 4, 2) +# ^^^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + +///// +// Type Conversions Functions +///// + + tobool(true) +# ^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^^^ meta.function-call.terraform constant.language.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + tolist(["a", "b", "c"]) +# ^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + tomap({a = 1, b = 2}) +# ^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.braces.begin.terraform +# ^ meta.function-call.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.function-call.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.function-call.terraform meta.braces.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.function-call.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.function-call.terraform meta.braces.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform meta.braces.terraform punctuation.section.braces.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + tonumber(1) +# ^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + toset(["a", "b", "c"]) +# ^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.brackets.end.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + tostring("hello") +# ^^^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^ meta.function-call.terraform string.quoted.double.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + + + provider::terraform::encode_tfvars({ +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.function-call.terraform +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ support.function.builtin.terraform +# ^ punctuation.section.parens.begin.terraform +# ^ meta.braces.terraform punctuation.section.braces.begin.terraform + example = "Hello!" + }) + +///////////////////////////////////////////////////////////////////// +// TUPLE FOR-EXPRESSIONS +///////////////////////////////////////////////////////////////////// + +///// +// Basic expression. +///// + + [for s in var.list : upper(s)] +# ^ punctuation.section.brackets.begin.terraform +# ^^^ keyword.control.loop.for.terraform +# ^ variable.other.readwrite.terraform +# ^^ keyword.control.loop.in.terraform +# ^^^ variable.language.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^ variable.other.member.terraform +# ^ punctuation.section.block.loop.for.terraform +# ^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ punctuation.section.brackets.end.terraform + +///// +// Object or map source value. +//// + + [for k, v in var.map : length(k) + length(v)] +# ^ punctuation.section.brackets.begin.terraform +# ^^^ keyword.control.loop.for.terraform +# ^ variable.other.readwrite.terraform +# ^ punctuation.separator.terraform +# ^ variable.other.readwrite.terraform +# ^^ keyword.control.loop.in.terraform +# ^^^ variable.language.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^ variable.other.member.terraform +# ^ punctuation.section.block.loop.for.terraform +# ^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ keyword.operator.arithmetic.terraform +# ^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ punctuation.section.brackets.end.terraform + +///// +// Complex right-side expressions. +//// + + [for o in var.list : o.interfaces[0].name] +# ^ punctuation.section.brackets.begin.terraform +# ^^^ keyword.control.loop.for.terraform +# ^ variable.other.readwrite.terraform +# ^^ keyword.control.loop.in.terraform +# ^^^ variable.language.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^ variable.other.member.terraform +# ^ punctuation.section.block.loop.for.terraform +# ^ variable.other.readwrite.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^^^^ variable.other.member.terraform +# ^ punctuation.section.brackets.begin.terraform +# ^ constant.numeric.integer.terraform +# ^ punctuation.section.brackets.end.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^ variable.other.member.terraform +# ^ punctuation.section.brackets.end.terraform + +///// +// Legacy splat expression attribute access. +///// + + [for o in var.list : o.interfaces][0].name +# ^ punctuation.section.brackets.begin.terraform +# ^^^ keyword.control.loop.for.terraform +# ^ variable.other.readwrite.terraform +# ^^ keyword.control.loop.in.terraform +# ^^^ variable.language.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^ variable.other.member.terraform +# ^ punctuation.section.block.loop.for.terraform +# ^ variable.other.readwrite.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^^^^ variable.other.member.terraform +# ^ punctuation.section.brackets.end.terraform +# ^ punctuation.section.brackets.begin.terraform +# ^ constant.numeric.integer.terraform +# ^ punctuation.section.brackets.end.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^ variable.other.member.terraform + +///// +// Multi-line for-expressions. +///// + + value = [ +# ^^^^^ variable.declaration.terraform variable.other.readwrite.terraform +# ^ keyword.operator.assignment.terraform +# ^ punctuation.section.brackets.begin.terraform + for instance in aws_instance.ubuntu: +# ^^^ keyword.control.loop.for.terraform +# ^^^^^^^^ variable.other.readwrite.terraform +# ^^ keyword.control.loop.in.terraform +# ^^^^^^^^^^^^ variable.other.readwrite.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^ variable.other.member.terraform +# ^ punctuation.section.block.loop.for.terraform + instance.private_dns +# ^^^^^^^^ variable.other.readwrite.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^^^^^ variable.other.member.terraform + ] +# ^ punctuation.section.brackets.end.terraform + +///// +// Match conditional on right-side expression. +///// + + value = [ +# ^^^^^ variable.declaration.terraform variable.other.readwrite.terraform +# ^ keyword.operator.assignment.terraform +# ^ punctuation.section.brackets.begin.terraform + for instance in aws_instance.ubuntu: +# ^^^ keyword.control.loop.for.terraform +# ^^^^^^^^ variable.other.readwrite.terraform +# ^^ keyword.control.loop.in.terraform +# ^^^^^^^^^^^^ variable.other.readwrite.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^ variable.other.member.terraform +# ^ punctuation.section.block.loop.for.terraform + (instance.public_ip != "" ? list(instance.private_ip, instance.public_ip) : list(instance.private_ip)) +# ^ punctuation.section.parens.begin.terraform +# ^^^^^^^^ variable.other.readwrite.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^^^ variable.other.member.terraform +# ^^ keyword.operator.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.end.terraform +# ^ keyword.operator.terraform +# ^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^^^^^^^ meta.function-call.terraform +# ^ meta.function-call.terraform punctuation.accessor.dot.terraform +# ^^^^^^^^^^ meta.function-call.terraform variable.other.member.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^^^^^^^^ meta.function-call.terraform +# ^ meta.function-call.terraform punctuation.accessor.dot.terraform +# ^^^^^^^^^ meta.function-call.terraform variable.other.member.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ keyword.operator.terraform +# ^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^^^^^^^ meta.function-call.terraform +# ^ meta.function-call.terraform punctuation.accessor.dot.terraform +# ^^^^^^^^^^ meta.function-call.terraform variable.other.member.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform +# ^ punctuation.section.parens.end.terraform + ] +# ^ punctuation.section.brackets.end.terraform + +///// +// Match brackets on right-side expression. +///// + + value = [ +# ^^^^^ variable.declaration.terraform variable.other.readwrite.terraform +# ^ keyword.operator.assignment.terraform +# ^ punctuation.section.brackets.begin.terraform + for instance in aws_instance.ubuntu: +# ^^^ keyword.control.loop.for.terraform +# ^^^^^^^^ variable.other.readwrite.terraform +# ^^ keyword.control.loop.in.terraform +# ^^^^^^^^^^^^ variable.other.readwrite.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^ variable.other.member.terraform +# ^ punctuation.section.block.loop.for.terraform + (instance.public_ip != "" ? [instance.private_ip, instance.public_ip] : [instance.private_ip]) +# ^ punctuation.section.parens.begin.terraform +# ^^^^^^^^ variable.other.readwrite.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^^^ variable.other.member.terraform +# ^^ keyword.operator.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.end.terraform +# ^ keyword.operator.terraform +# ^ punctuation.section.brackets.begin.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^^^^ variable.other.member.terraform +# ^ punctuation.separator.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^^^ variable.other.member.terraform +# ^ punctuation.section.brackets.end.terraform +# ^ keyword.operator.terraform +# ^ punctuation.section.brackets.begin.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^^^^ variable.other.member.terraform +# ^ punctuation.section.brackets.end.terraform +# ^ punctuation.section.parens.end.terraform + ] +# ^ punctuation.section.brackets.end.terraform + +///// +// Match if-conditionals on right-side. +///// + + [for s in var.list : upper(s) if s != ""] +# ^ punctuation.section.brackets.begin.terraform +# ^^^ keyword.control.loop.for.terraform +# ^ variable.other.readwrite.terraform +# ^^ keyword.control.loop.in.terraform +# ^^^ variable.language.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^ variable.other.member.terraform +# ^ punctuation.section.block.loop.for.terraform +# ^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform +# ^^ keyword.control.conditional.terraform +# ^ variable.other.readwrite.terraform +# ^^ keyword.operator.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.end.terraform +# ^ punctuation.section.brackets.end.terraform + +///// +// Matches bracket-literals as range expression. +///// + + [for i, v in ["a", "b", "c"]: v if i < 2] +# ^ punctuation.section.brackets.begin.terraform +# ^^^ keyword.control.loop.for.terraform +# ^ variable.other.readwrite.terraform +# ^ punctuation.separator.terraform +# ^ variable.other.readwrite.terraform +# ^^ keyword.control.loop.in.terraform +# ^ punctuation.section.brackets.begin.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ string.quoted.double.terraform +# ^ punctuation.separator.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ string.quoted.double.terraform +# ^ punctuation.separator.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ string.quoted.double.terraform +# ^ punctuation.section.brackets.end.terraform +# ^ punctuation.section.block.loop.for.terraform +# ^ variable.other.readwrite.terraform +# ^^ keyword.control.conditional.terraform +# ^ variable.other.readwrite.terraform +# ^ keyword.operator.terraform +# ^ constant.numeric.integer.terraform +# ^ punctuation.section.brackets.end.terraform + +///////////////////////////////////////////////////////////////////// +// OBJECT FOR-EXPRESSIONS +///////////////////////////////////////////////////////////////////// + +///// +// Matches basic syntax. +///// + + {for i, v in ["a", "b"]: v => i} +# ^ meta.braces.terraform punctuation.section.braces.begin.terraform +# ^^^ meta.braces.terraform keyword.control.loop.for.terraform +# ^ variable.other.readwrite.terraform +# ^ punctuation.separator.terraform +# ^ variable.other.readwrite.terraform +# ^^ keyword.control.loop.in.terraform +# ^ punctuation.section.brackets.begin.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ string.quoted.double.terraform +# ^ punctuation.separator.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ string.quoted.double.terraform +# ^ punctuation.section.brackets.end.terraform +# ^ punctuation.section.block.loop.for.terraform +# ^ variable.other.readwrite.terraform +# ^^ punctuation.separator.key-value.terraform +# ^ variable.other.readwrite.terraform +# ^ punctuation.section.braces.end.terraform +# ^ -meta + +///// +// Matches ellipsis. +///// + + {for i, v in ["a"]: v => i...} +# ^ meta.braces.terraform punctuation.section.braces.begin.terraform +# ^^^ meta.braces.terraform keyword.control.loop.for.terraform +# ^ variable.other.readwrite.terraform +# ^ punctuation.separator.terraform +# ^ variable.other.readwrite.terraform +# ^^ keyword.control.loop.in.terraform +# ^ punctuation.section.brackets.begin.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^ string.quoted.double.terraform +# ^ punctuation.section.brackets.end.terraform +# ^ punctuation.section.block.loop.for.terraform +# ^ variable.other.readwrite.terraform +# ^^ punctuation.separator.key-value.terraform +# ^ variable.other.readwrite.terraform +# ^^^ keyword.operator.terraform +# ^ punctuation.section.braces.end.terraform + +///// +// Matches if-conditional. +///// + + {for s in var.list : substr(s, 0, 1) => s... if s != ""} +# ^ meta.braces.terraform punctuation.section.braces.begin.terraform +# ^^^ meta.braces.terraform keyword.control.loop.for.terraform +# ^ variable.other.readwrite.terraform +# ^^ keyword.control.loop.in.terraform +# ^^^ variable.language.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^ variable.other.member.terraform +# ^ punctuation.section.block.loop.for.terraform +# ^^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.separator.terraform +# ^ meta.function-call.terraform constant.numeric.integer.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform +# ^^ punctuation.separator.key-value.terraform +# ^ variable.other.readwrite.terraform +# ^^^ keyword.operator.terraform +# ^^ keyword.control.conditional.terraform +# ^ variable.other.readwrite.terraform +# ^^ keyword.operator.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^ string.quoted.double.terraform punctuation.definition.string.end.terraform +# ^ punctuation.section.braces.end.terraform + +///// +// Matches over multiple-lines. +///// + + value = { +# ^^^^^ variable.declaration.terraform variable.other.readwrite.terraform +# ^ keyword.operator.assignment.terraform +# ^ meta.braces.terraform punctuation.section.braces.begin.terraform + for l in var.letters: l => +# ^^^ meta.braces.terraform keyword.control.loop.for.terraform +# ^ variable.other.readwrite.terraform +# ^^ keyword.control.loop.in.terraform +# ^^^ variable.language.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^ variable.other.member.terraform +# ^ punctuation.section.block.loop.for.terraform +# ^ variable.other.readwrite.terraform +# ^^ punctuation.separator.key-value.terraform + upper(l) +# ^^^^^ meta.function-call.terraform support.function.builtin.terraform +# ^ meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^ meta.function-call.terraform +# ^ meta.function-call.terraform punctuation.section.parens.end.terraform + } +# ^ punctuation.section.braces.end.terraform + +///////////////////////////////////////////////////////////////////// +// BLOCKS +///////////////////////////////////////////////////////////////////// + +///// +// Inline block with no labels. +//// + + thing {} +# ^^^^^ meta.type.terraform entity.name.type.terraform +# ^ meta.type.terraform meta.block.terraform punctuation.section.block.begin.terraform +# ^ meta.block.terraform punctuation.section.block.end.terraform + +///// +// Inline block with string labels. +///// + + thing "label1" "label2" {} +# ^^^^^ meta.type.terraform entity.name.type.terraform +# ^ meta.type.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^ meta.type.terraform string.quoted.double.terraform +# ^ meta.type.terraform string.quoted.double.terraform punctuation.definition.string.end.terraform +# ^ meta.type.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^ meta.type.terraform string.quoted.double.terraform +# ^ meta.type.terraform string.quoted.double.terraform punctuation.definition.string.end.terraform +# ^ meta.type.terraform meta.block.terraform punctuation.section.block.begin.terraform +# ^ meta.block.terraform punctuation.section.block.end.terraform +# ^ -meta + +///// +// Inline block with identifier labels. +///// + + thing thing1 thing2 thing3 {} +# ^^^^^ meta.type.terraform entity.name.type.terraform +# ^^^^^^ meta.type.terraform entity.name.label.terraform +# ^^^^^^ meta.type.terraform entity.name.label.terraform +# ^^^^^^ meta.type.terraform entity.name.label.terraform +# ^ meta.type.terraform meta.block.terraform punctuation.section.block.begin.terraform +# ^ meta.block.terraform punctuation.section.block.end.terraform + +////// +// Nested multi-line blocks with expressions. +///// + + resource "aws_security_group" "example" { +# ^^^^^^^^ meta.type.terraform keyword.declaration.terraform +# ^ meta.type.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^^^^^^^^^^^^ meta.type.terraform string.quoted.double.terraform +# ^ meta.type.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^ meta.type.terraform string.quoted.double.terraform +# ^ meta.type.terraform meta.block.terraform punctuation.section.block.begin.terraform + name = "example" +# ^^^^ meta.block.terraform variable.declaration.terraform variable.other.readwrite.terraform +# ^ meta.block.terraform keyword.operator.assignment.terraform +# ^ meta.block.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^ meta.block.terraform string.quoted.double.terraform + + dynamic "ingress" { +# ^^^^^^^ meta.block.terraform meta.type.terraform entity.name.type.terraform +# ^ meta.block.terraform meta.type.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^^^ meta.block.terraform meta.type.terraform string.quoted.double.terraform +# ^ meta.block.terraform meta.type.terraform meta.block.terraform punctuation.section.block.begin.terraform + for_each = var.service_ports +# ^^^^^^^^ variable.declaration.terraform keyword.control.loop.for.terraform +# ^ keyword.operator.assignment.terraform +# ^^^ variable.language.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^^^^^^^ variable.other.member.terraform + content { +# ^^^^^^^ meta.block.terraform meta.block.terraform meta.type.terraform entity.name.type.terraform +# ^ meta.block.terraform meta.block.terraform meta.type.terraform meta.block.terraform punctuation.section.block.begin.terraform + from_port = ingress.value +# ^^^^^^^^^ meta.block.terraform meta.block.terraform meta.block.terraform variable.declaration.terraform variable.other.readwrite.terraform +# ^ meta.block.terraform meta.block.terraform meta.block.terraform keyword.operator.assignment.terraform +# ^ meta.block.terraform meta.block.terraform meta.block.terraform punctuation.accessor.dot.terraform +# ^^^^^ meta.block.terraform meta.block.terraform meta.block.terraform variable.other.member.terraform + to_port = ingress.value +# ^^^^^^^ meta.block.terraform meta.block.terraform meta.block.terraform variable.declaration.terraform variable.other.readwrite.terraform +# ^ meta.block.terraform meta.block.terraform meta.block.terraform keyword.operator.assignment.terraform +# ^ meta.block.terraform meta.block.terraform meta.block.terraform punctuation.accessor.dot.terraform + protocol = "tcp" + "IP" +# ^^^^^^^^ meta.block.terraform meta.block.terraform meta.block.terraform variable.declaration.terraform variable.other.readwrite.terraform +# ^ meta.block.terraform meta.block.terraform meta.block.terraform keyword.operator.assignment.terraform +# ^ meta.block.terraform meta.block.terraform meta.block.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^ meta.block.terraform meta.block.terraform meta.block.terraform string.quoted.double.terraform +# ^ meta.block.terraform meta.block.terraform meta.block.terraform keyword.operator.arithmetic.terraform +# ^ meta.block.terraform meta.block.terraform meta.block.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^ meta.block.terraform meta.block.terraform meta.block.terraform string.quoted.double.terraform + } +# ^ meta.block.terraform meta.block.terraform meta.block.terraform punctuation.section.block.end.terraform + } +# ^ meta.block.terraform meta.block.terraform punctuation.section.block.end.terraform + } +# ^ meta.block.terraform punctuation.section.block.end.terraform +# ^ -meta + +///// +// Matches blocks with functions, objects, tuples. +///// + + thing label1 { +# ^^^^^ meta.type.terraform entity.name.type.terraform +# ^^^^^^ meta.type.terraform entity.name.label.terraform +# ^ meta.type.terraform meta.block.terraform punctuation.section.block.begin.terraform + func = function(param1) +# ^^^^ meta.block.terraform variable.declaration.terraform variable.other.readwrite.terraform +# ^ meta.block.terraform keyword.operator.assignment.terraform +# ^^^^^^^^ meta.block.terraform meta.function-call.terraform variable.function.terraform +# ^ meta.block.terraform meta.function-call.terraform punctuation.section.parens.begin.terraform +# ^^^^^^ meta.block.terraform meta.function-call.terraform +# ^ meta.block.terraform meta.function-call.terraform punctuation.section.parens.end.terraform + obj = { +# ^^^ meta.block.terraform variable.declaration.terraform variable.other.readwrite.terraform +# ^ meta.block.terraform keyword.operator.assignment.terraform + key = "value" +# ^^^ meta.block.terraform meta.braces.terraform meta.mapping.key.terraform string.unquoted.terraform +# ^ meta.block.terraform meta.braces.terraform keyword.operator.assignment.terraform +# ^ meta.block.terraform meta.braces.terraform string.quoted.double.terraform punctuation.definition.string.begin.terraform +# ^^^^^^ meta.block.terraform meta.braces.terraform string.quoted.double.terraform +# ^ meta.block.terraform meta.braces.terraform string.quoted.double.terraform punctuation.definition.string.end.terraform + } +# ^ meta.block.terraform meta.braces.terraform punctuation.section.braces.end.terraform + tuple = [1, 2] +# ^^^^^ meta.block.terraform variable.declaration.terraform variable.other.readwrite.terraform +# ^ meta.block.terraform keyword.operator.assignment.terraform +# ^ meta.block.terraform punctuation.section.brackets.begin.terraform +# ^ meta.block.terraform constant.numeric.integer.terraform +# ^ meta.block.terraform punctuation.separator.terraform +# ^ meta.block.terraform constant.numeric.integer.terraform +# ^ meta.block.terraform punctuation.section.brackets.end.terraform + } +# ^ meta.block.terraform punctuation.section.block.end.terraform +# ^ -meta -block + +///////////////////////////////////////////////////////////////////// +// TERRAFORM NAMED VALUES +///////////////////////////////////////////////////////////////////// + + var.something +# ^^^ variable.language.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^^^ variable.other.member.terraform + + local.something +# ^^^^^ variable.language.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^^^ variable.other.member.terraform + + module.name.output_name +# ^^^^^^ variable.language.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^ variable.other.member.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^^^^^ variable.other.member.terraform + + data.data_type.name +# ^^^^ variable.language.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^^^ variable.other.member.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^ variable.other.member.terraform + + path.module +# ^^^^ variable.language.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^ variable.other.member.terraform + + terraform.workspace +# ^^^^^^^^^ variable.language.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^^^ variable.other.member.terraform + + count.index +# ^^^^^ variable.language.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^ variable.other.member.terraform + + each.key +# ^^^^ variable.language.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^ variable.other.member.terraform + + self.private_ip +# ^^^^ variable.language.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^^^^ variable.other.member.terraform + +///////////////////////////////////////////////////////////////////// +// TERRAFORM TOP-LEVEL BLOCK TYPES +///////////////////////////////////////////////////////////////////// + + resource {} +# ^^^^^^^^ meta.type.terraform keyword.declaration.terraform +# ^ meta.type.terraform meta.block.terraform punctuation.section.block.begin.terraform +# ^ meta.block.terraform punctuation.section.block.end.terraform + + provider {} +# ^^^^^^^^ meta.type.terraform keyword.declaration.terraform +# ^ meta.type.terraform meta.block.terraform punctuation.section.block.begin.terraform +# ^ meta.block.terraform punctuation.section.block.end.terraform + + variable {} +# ^^^^^^^^ meta.type.terraform keyword.declaration.terraform +# ^ meta.type.terraform meta.block.terraform punctuation.section.block.begin.terraform +# ^ meta.block.terraform punctuation.section.block.end.terraform + + output {} +# ^^^^^^ meta.type.terraform keyword.declaration.terraform +# ^ meta.type.terraform meta.block.terraform punctuation.section.block.begin.terraform +# ^ meta.block.terraform punctuation.section.block.end.terraform + + locals {} +# ^^^^^^ meta.type.terraform keyword.declaration.terraform +# ^ meta.type.terraform meta.block.terraform punctuation.section.block.begin.terraform +# ^ meta.block.terraform punctuation.section.block.end.terraform + + module {} +# ^^^^^^ meta.type.terraform keyword.declaration.terraform +# ^ meta.type.terraform meta.block.terraform punctuation.section.block.begin.terraform +# ^ meta.block.terraform punctuation.section.block.end.terraform + + data {} +# ^^^^ meta.type.terraform keyword.declaration.terraform +# ^ meta.type.terraform meta.block.terraform punctuation.section.block.begin.terraform +# ^ meta.block.terraform punctuation.section.block.end.terraform + + terraform {} +# ^^^^^^^^^ meta.type.terraform keyword.declaration.terraform +# ^ meta.type.terraform meta.block.terraform punctuation.section.block.begin.terraform +# ^ meta.block.terraform punctuation.section.block.end.terraform + +///////////////////////////////////////////////////////////////////// +// TERRAFORM TYPE KEYWORDS +///////////////////////////////////////////////////////////////////// + + string +# ^^^^^^ storage.type.terraform + + any +# ^^^ storage.type.terraform + + number +# ^^^^^^ storage.type.terraform + + bool +# ^^^^ storage.type.terraform + +///////////////////////////////////////////////////////////////////// +// HEREDOCS +///////////////////////////////////////////////////////////////////// + +///// +// Basic example. +///// + << EOF +# ^^ keyword.operator.heredoc.terraform +# ^^^ keyword.control.heredoc.terraform + sdfdfsd +# ^^^^^^^^ string.unquoted.heredoc.terraform + EOF +# ^^^^ keyword.control.heredoc.terraform + +///// +// With leading-spaces-operator. +///// + + <<- END +# ^^^ keyword.operator.heredoc.terraform +# ^^^ keyword.control.heredoc.terraform + heredoc +# ^^^^^^^^ string.unquoted.heredoc.terraform + EOF +# ^^^^ string.unquoted.heredoc.terraform + END +# ^^^^ keyword.control.heredoc.terraform + +///// +// Includes string interpolation. +///// + + <<- END +# ^^^ keyword.operator.heredoc.terraform +# ^^^ keyword.control.heredoc.terraform + Hello, %{var.name} +#^^^^^^^^^^^^^^^^^^^^^ meta.string.terraform +#^^^^^^^^^^ string.unquoted.heredoc.terraform +# ^^^^^^^^^^^ meta.interpolation.terraform +# ^^ punctuation.section.interpolation.begin.terraform +# ^^^^^^^^ source.terraform +# ^^^ variable.language.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^ variable.other.member.terraform +# ^ punctuation.section.interpolation.end.terraform + END +# ^^^^ keyword.control.heredoc.terraform + + <<__EOF__ +# ^^ keyword.operator.heredoc.terraform +# ^^^^^^^ keyword.control.heredoc.terraform + aaa + __EOF__ +# ^^^^^^^^ keyword.control.heredoc.terraform +///////////////////////////////////////////////////////////////////// +// IMPORTS +///////////////////////////////////////////////////////////////////// + +///// +// Import with attribute access. +///// + + terraform import aws_instance.example i-abcd1234 +# ^^^^^^^^^ support.constant.terraform +# ^^^^^^ keyword.control.import.terraform +# ^^^^^^^^^^^^ entity.name.label.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^^^ variable.other.member.terraform +# ^^^^^^^^^^ entity.name.label.terraform + +///// +// Import with numeric literals. +///// + + terraform import digitalocean_ssh_key.mykey 263654 +# ^^^^^^^^^ support.constant.terraform +# ^^^^^^ keyword.control.import.terraform +# ^^^^^^^^^^^^^^^^^^^^ entity.name.label.terraform +# ^ punctuation.accessor.dot.terraform +# ^^^^^ variable.other.member.terraform +# ^^^^^^ constant.numeric.integer.terraform + +variable "instance_type" { + default = "t2.micro" +} + +resource "aws_instance" "example" { + ami = "ami-123456" + instance_type = var.instance_type +} + +data "aws_ami" "example" { + most_recent = true + owners = ["self"] + + filter { + name = "name" + values = ["my-ami-*"] + } +} + +resource "aws_instance" "example" { + ami = data.aws_ami.example.id + instance_type = "t2.micro" +} diff --git a/extensions/terraform-mode/indent.lisp b/extensions/terraform-mode/indent.lisp new file mode 100644 index 000000000..ac8ad3217 --- /dev/null +++ b/extensions/terraform-mode/indent.lisp @@ -0,0 +1,184 @@ +;; copied from c-mode + +(defpackage :lem-terraform-mode/indent + (:use :cl + :lem + :lem/language-mode) + (:export :beginning-of-defun + :end-of-defun + :calc-indent)) +(in-package :lem-terraform-mode/indent) + +(defvar *indent-line-function* nil) + +(defun beginning-of-defun (point n) + (loop :repeat n :do (search-backward-regexp point "^\\w[^=(]*"))) + +(defun end-of-defun (point n) + (if (minusp n) + (beginning-of-defun point (- n)) + (search-forward-regexp point "^\\};*"))) + +(defun %indent (p indent) + (when *indent-line-function* + (funcall *indent-line-function* p indent))) + +(defun delimiter-line-p (p) + (multiple-value-bind (start) + (ppcre:scan "[^\\\\]?;\\s*(?:/\\*.*?\\*/|//.*?)?\\s*$" (line-string p)) + (when start + (with-point ((p p)) + (line-offset p 0 (1+ start)) + (not (in-string-or-comment-p p)))))) + +(defun end-block-line-p (p) + (with-point ((p p)) + (loop :for start := 0 :then (1+ i) + :for i := (position #\} (line-string p) :start start) + :while i + :do (unless (let ((p (character-offset (line-start p) i))) + (check-type p point) + (in-string-or-comment-p p)) + (return i))))) + +(defun dangling-start-p (p) + (let ((str (looking-at p "(?:do|else\\s+if|else|for|if|switch|while)\\s*"))) + (character-offset p (length str)) + (or (not (eql #\( (character-at p))) + (scan-lists p 1 0 t)) + (let ((old-linenumber (line-number-at-point p))) + (skip-space-and-comment-forward p) + (/= old-linenumber (line-number-at-point p))))) + +(defun unbalanced-p (state) + (if (member #\( (pps-state-paren-stack state)) t nil)) + +(defun unbalanced-indent (p indent start) + (flet ((jmp-start-paren (p) + (loop + (scan-lists p -1 1) + (when (eql #\( (character-at p)) + (return))))) + (let ((state)) + (%indent p indent) + (jmp-start-paren p) + (let ((indent1 (1+ (point-column p)))) + (loop + (unless (line-offset p 1) (return-from unbalanced-indent nil)) + (%indent p indent1) + (unless (unbalanced-p (setf state + (parse-partial-sexp (copy-point start :temporary) + (line-end p)))) + (return)) + (with-point ((p p)) + (jmp-start-paren p) + (setf indent1 (1+ (point-column p))))) + state)))) + +(defun cond-op-line-p (p limit) + (and (not (delimiter-line-p p)) + (search-forward (line-start p) "?" limit) + (not (in-string-or-comment-p p)) + (not (syntax-escape-char-p (character-at p -2))))) + +(defun indent-cond-op (p indent) + (with-point ((tmp (line-end p))) + (when (cond-op-line-p p tmp) + (line-start tmp) + (when (and (not (unbalanced-p (parse-partial-sexp tmp p))) + (not (delimiter-line-p p))) + (loop + (unless (line-offset p 1) (return-from indent-cond-op nil)) + (c-indent-line p (+ indent (variable-value 'indent-size :default p))) + (when (delimiter-line-p p) + (return)))))) + t) + +(defun c-indent-line (p indent) + (let ((indent-size (variable-value 'indent-size :default p))) + (back-to-indentation p) + (loop :while (end-line-p p) + :do (%indent p indent) + :do (if (line-offset p 1) + (back-to-indentation p) + (return-from c-indent-line nil))) + (when (eql #\# (character-at p)) + (%indent p 0)) + (when (eql #\} (character-at p)) + (character-offset p 1) + (skip-whitespace-forward p t)) + (alexandria:when-let ((i (end-block-line-p p))) + (with-point ((p p) + (start p)) + (line-start start) + (character-offset (line-start p) (1+ i)) + (when (> 0 (pps-state-paren-depth (parse-partial-sexp start p))) + (decf indent indent-size)))) + (let ((word (looking-at p "\\w+")) + (word-point) + (state)) + (when word + (setf word-point (copy-point p :temporary)) + (character-offset p (length word)) + (skip-whitespace-forward p t)) + (with-point ((start p)) + (line-start start) + (setf state (parse-partial-sexp (copy-point start :temporary) + (line-end p))) + (cond + ((unbalanced-p state) + (unless (setf state (unbalanced-indent p indent start)) + (return-from c-indent-line nil))) + #+(or)((and word (ppcre:scan "^(?:case|default)$" word)) + (%indent p (- indent indent-size))) + (t + (%indent p indent) + (unless (indent-cond-op p indent) + (return-from c-indent-line nil))))) + (when (eql #\{ (car (pps-state-paren-stack state))) + (let ((indent (+ indent indent-size)) + (status)) + (loop + (unless (line-offset p 1) (return-from c-indent-line nil)) + (setf (values indent status) (c-indent-line p indent)) + (when (and (not (eq status :block-end)) + (end-block-line-p p)) + (return-from c-indent-line (values indent :block-end)))))) + (when (and word-point (dangling-start-p word-point)) + (unless (line-offset p 1) (return-from c-indent-line nil)) + (c-indent-line p (+ indent indent-size)) + (return-from c-indent-line indent)) + (return-from c-indent-line indent)))) + +(defun calc-indent-region (start end) + (with-point ((p start)) + (let ((indent (point-column (back-to-indentation p)))) + (loop + (let ((next-indent (c-indent-line p indent))) + (unless next-indent (return)) + (unless (line-offset p 1) (return)) + (unless (point< start end) (return)) + (setf indent next-indent)))))) + +(defun calc-indent (point) + (cond + ((in-string-or-comment-p point) + (with-point ((p point)) + (back-to-indentation p) + (if (in-string-or-comment-p p) + (point-column p) + (calc-indent p)))) + ((with-point ((p point)) + (when (maybe-beginning-of-comment p) + (if (eql #\* (character-at (back-to-indentation point))) + (+ 1 (point-column p)) + (+ 2 (point-column p)))))) + (t + (with-point ((start point)) + (line-offset start -1) + (beginning-of-defun start 1) + (let ((*indent-line-function* + (lambda (p indent) + (when (same-line-p point p) + (return-from calc-indent indent))))) + (calc-indent-region start point)))))) diff --git a/extensions/terraform-mode/lem-terraform-mode.asd b/extensions/terraform-mode/lem-terraform-mode.asd new file mode 100644 index 000000000..91bea7329 --- /dev/null +++ b/extensions/terraform-mode/lem-terraform-mode.asd @@ -0,0 +1,6 @@ +(defsystem "lem-terraform-mode" + :depends-on ("lem") + :serial t + :components ((:file "indent") + (:file "terraform-mode") + (:file "lsp-config"))) diff --git a/extensions/terraform-mode/lsp-config.lisp b/extensions/terraform-mode/lsp-config.lisp new file mode 100644 index 000000000..6d29d9dca --- /dev/null +++ b/extensions/terraform-mode/lsp-config.lisp @@ -0,0 +1,13 @@ +(defpackage :lem-terraform-mode/lsp-config + (:use :cl + :lem-lsp-mode + :lem-lsp-base/type)) +(in-package :lem-terraform-mode/lsp-config) + +(define-language-spec (terraform-spec lem-terraform-mode:terraform-mode) + :language-id "terraform" + :root-uri-patterns '() + :command (lambda (port) `("terraform-ls" "serve" "-port" ,(princ-to-string port))) + :install-command "" + :readme-url "https://github.com/hashicorp/terraform-ls" + :connection-mode :tcp) diff --git a/extensions/terraform-mode/terraform-mode.lisp b/extensions/terraform-mode/terraform-mode.lisp new file mode 100644 index 000000000..ed3d56e96 --- /dev/null +++ b/extensions/terraform-mode/terraform-mode.lisp @@ -0,0 +1,95 @@ +(defpackage :lem-terraform-mode + (:use :cl + :lem + :lem/language-mode + :lem/language-mode-tools) + (:export :terraform-mode)) +(in-package :lem-terraform-mode) + +(defun tokens (boundary strings) + (let ((alternation + `(:alternation ,@(sort (copy-list strings) #'> :key #'length)))) + (if boundary + `(:sequence ,boundary ,alternation ,boundary) + alternation))) + +(defun make-tmlanguage-terraform () + (let ((patterns (make-tm-patterns + (make-tm-region '(:sequence "//") "$" :name 'syntax-comment-attribute) + (make-tm-region '(:sequence "#") "$" :name 'syntax-comment-attribute) + (make-tm-region '(:sequence "/*") + '(:sequence "*/") + :name 'syntax-comment-attribute) + (make-tm-match (tokens :word-boundary + '("provider" + "resource" + "data" + "module" + "variable" + "output" + "locals" + "terraform" + "count" + "depends_on" + "for_each" + "lifecycle" + "provisioner" + "connection" + "sensitive" + "ignore_changes" + "create_before_destroy" + "prevent_destroy" + "dynamic" + "true" + "false" + "null" + "if" + "else" + "for" + "in" + "map" + "list" + "object" + "var" + "local" + "each" + "self" + "path" + "zipmap" + "cidrsubnet" + "element" + "lookup")) + :name 'syntax-keyword-attribute) + (make-tm-string-region "\"") + (make-tm-string-region "'") + (make-tm-string-region "\"\"\"") + (make-tm-string-region "'''")))) + (make-tmlanguage :patterns patterns))) + +(defvar *syntax-table* + (let ((table (make-syntax-table + :space-chars '(#\space #\tab #\newline) + :symbol-chars '(#\_) + :paren-pairs '((#\( . #\)) + (#\{ . #\}) + (#\[ . #\])) + :string-quote-chars '(#\" #\' #\`) + :line-comment-string "//" + :block-comment-pairs '(("/*" . "*/"))))) + (set-syntax-parser table (make-tmlanguage-terraform)) + table)) + +(define-major-mode terraform-mode language-mode + (:name "Terraform" + :keymap *terraform-mode-keymap* + :syntax-table *syntax-table* + :mode-hook *terraform-mode-hook*) + (setf (variable-value 'enable-syntax-highlight) t + (variable-value 'indent-tabs-mode) nil + (variable-value 'tab-width) 2 + (variable-value 'calc-indent-function) 'lem-terraform-mode/indent::calc-indent + (variable-value 'line-comment) "//" + (variable-value 'beginning-of-defun-function) 'lem-terraform-mode/indent:beginning-of-defun + (variable-value 'end-of-defun-function) 'lem-terraform-mode/indent:end-of-defun)) + +(define-file-type ("tf") terraform-mode) diff --git a/extensions/vi-mode/binds.lisp b/extensions/vi-mode/binds.lisp index 6e51c0b70..c6dffc20c 100644 --- a/extensions/vi-mode/binds.lisp +++ b/extensions/vi-mode/binds.lisp @@ -44,8 +44,8 @@ (define-key *motion-keymap* "H" 'vi-move-to-window-top) (define-key *motion-keymap* "M" 'vi-move-to-window-middle) (define-key *motion-keymap* "L" 'vi-move-to-window-bottom) -(define-key *motion-keymap* "C-d" 'next-page) -(define-key *motion-keymap* "C-u" 'previous-page) +(define-key *motion-keymap* "C-d" 'vi-scroll-down) +(define-key *motion-keymap* "C-u" 'vi-scroll-up) (define-key *motion-keymap* "^" 'vi-back-to-indentation) (define-key *motion-keymap* "_" 'vi-back-to-indentation) (define-key *motion-keymap* "{" 'backward-paragraph) diff --git a/extensions/vi-mode/commands.lisp b/extensions/vi-mode/commands.lisp index ab6cfef8e..42d1eddc4 100644 --- a/extensions/vi-mode/commands.lisp +++ b/extensions/vi-mode/commands.lisp @@ -65,6 +65,8 @@ :vi-scroll-line-to-bottom-back-to-indentation :vi-scroll-bottom-line-to-top :vi-scroll-top-line-to-bottom + :vi-scroll-down + :vi-scroll-up :vi-back-to-indentation :vi-indent :vi-substitute @@ -188,6 +190,20 @@ (:type :line) (previous-line n)) +(define-motion vi-scroll-down (&optional (n nil)) (:universal-nil) + (:type :inclusive :default-n-arg nil) + (unless n + (setf n (floor (window-height (current-window)) 2))) + (next-line n) + (scroll-down n)) + +(define-motion vi-scroll-up (&optional (n nil)) (:universal-nil) + (:default-n-arg nil) + (unless n + (setf n (floor (window-height (current-window)) 2))) + (previous-line n) + (scroll-up n)) + (defun on-only-space-line-p (point) (with-point ((p point)) (line-end p) @@ -417,15 +433,19 @@ Move the cursor to the first non-blank character of the line." (when (point= beg end) (return-from vi-change)) (let ((end-with-newline (char= (character-at end -1) #\Newline))) - (vi-delete beg end type) - (when (eq type :line) - (cond - (end-with-newline - (insert-character (current-point) #\Newline) - (character-offset (current-point) -1)) - (t - (insert-character (current-point) #\Newline))) - (indent-line (current-point)))) + (case type + (:line + (vi-delete beg end type) + (cond + (end-with-newline + (insert-character (current-point) #\Newline) + (character-offset (current-point) -1)) + (t + (insert-character (current-point) #\Newline))) + (indent-line (current-point))) + (t (unless (eql (character-at (current-point)) #\Space) + (skip-whitespace-backward end)) + (vi-delete beg end type)))) (change-state 'insert)) (define-operator vi-change-whole-line (beg end) ("") @@ -686,6 +706,16 @@ Move the cursor to the first non-blank character of the line." (editor-error "No keyboard macro is recorded yet")) (vi-execute-macro n *last-recorded-macro*)) +(defun find-next-paren (point) + "Returns the point either on the following opening/closing paren/bracket/brace +on the same line or at eol if there are none." + (with-point ((point point)) + (loop :until (or (syntax-open-paren-char-p (character-at point)) + (syntax-closed-paren-char-p (character-at point)) + (end-line-p point)) + :do (character-offset point 1)) + point)) + (defun vi-forward-matching-paren (window point &optional (offset -1)) (declare (ignore window)) (with-point ((point point)) @@ -695,8 +725,9 @@ Move the cursor to the first non-blank character of the line." (defun vi-backward-matching-paren (window point &optional (offset -1)) (declare (ignore window offset)) - (when (syntax-closed-paren-char-p (character-at point)) - (scan-lists (character-offset (copy-point point :temporary) 1) -1 0 t))) + (with-point ((point point)) + (when (syntax-closed-paren-char-p (character-at point)) + (scan-lists (character-offset (copy-point point :temporary) 1) -1 0 t)))) (define-motion vi-move-to-matching-item (&optional n) (:universal-nil) (:type :inclusive @@ -712,8 +743,9 @@ Move the cursor to the first non-blank character of the line." (skip-whitespace-forward (current-point) t))) ;; No argument - move to matching paren (t - (alexandria:when-let ((p (or (vi-backward-matching-paren (current-window) (current-point)) - (vi-forward-matching-paren (current-window) (current-point))))) + (alexandria:when-let* ((paren-point (find-next-paren (current-point))) + (p (or (vi-backward-matching-paren (current-window) paren-point) + (vi-forward-matching-paren (current-window) paren-point)))) (move-point (current-point) p))))) (let ((old-forward-matching-paren) diff --git a/extensions/vi-mode/ex-command.lisp b/extensions/vi-mode/ex-command.lisp index 915447956..591bed79a 100644 --- a/extensions/vi-mode/ex-command.lisp +++ b/extensions/vi-mode/ex-command.lisp @@ -9,16 +9,21 @@ (:import-from :lem-vi-mode/options :execute-set-command) (:import-from :lem-vi-mode/utils - :change-directory - :expand-filename-modifiers)) + :change-directory* + :expand-filename-modifiers) + (:export :*edit-buffer-directory*)) (in-package :lem-vi-mode/ex-command) +(defvar *edit-buffer-directory* nil) + (defun ex-edit (filename force) (if (string= filename "") (lem:revert-buffer force) (with-jumplist (lem:find-file (merge-pathnames (expand-filename-modifiers filename) - (uiop:getcwd)))))) + (if *edit-buffer-directory* + (lem:buffer-directory) + (uiop:getcwd))))))) (defun ex-write (range filename touch) (case (length range) @@ -49,6 +54,14 @@ (define-ex-command "^(w|write)$" (range filename) (ex-write range filename t)) +(define-ex-command "^wa(?:ll)$" (range argument) + (declare (ignore range argument)) + (ex-write-all nil)) + +(define-ex-command "^wa(?:ll)!$" (range argument) + (declare (ignore range argument)) + (ex-write-all t)) + (define-ex-command "^update$" (range filename) (when (lem:buffer-modified-p (lem:current-buffer)) (ex-write range filename t))) @@ -236,7 +249,7 @@ (define-ex-command "^cd$" (range new-directory) (declare (ignore range)) - (let ((new-directory (change-directory (expand-filename-modifiers new-directory)))) + (let ((new-directory (change-directory* (expand-filename-modifiers new-directory)))) (lem:message "~A" new-directory))) (define-ex-command "^noh(?:lsearch)?$" (range argument) diff --git a/extensions/vi-mode/options.lisp b/extensions/vi-mode/options.lisp index 76be0f727..56e349adc 100644 --- a/extensions/vi-mode/options.lisp +++ b/extensions/vi-mode/options.lisp @@ -3,7 +3,7 @@ :lem :split-sequence) (:import-from :lem-vi-mode/utils - :change-directory) + :change-directory*) (:import-from :parse-number :parse-number) (:import-from :cl-ppcre @@ -284,9 +284,9 @@ ',name)))))) (defun auto-change-directory (buffer-or-window) - (change-directory (etypecase buffer-or-window - (lem:buffer (lem:buffer-directory buffer-or-window)) - (lem:window (lem:buffer-directory (lem:window-buffer buffer-or-window)))))) + (change-directory* (etypecase buffer-or-window + (lem:buffer (lem:buffer-directory buffer-or-window)) + (lem:window (lem:buffer-directory (lem:window-buffer buffer-or-window)))))) (define-option "autochdir" (nil :type boolean :aliases ("acd")) (:documentation "Boolean to change the current directory to the buffer's directory automatically. diff --git a/extensions/vi-mode/utils.lisp b/extensions/vi-mode/utils.lisp index face11d38..7fa171d69 100644 --- a/extensions/vi-mode/utils.lisp +++ b/extensions/vi-mode/utils.lisp @@ -2,7 +2,7 @@ (:use :cl :lem) (:import-from :cl-ppcre) - (:export :change-directory + (:export :change-directory* :expand-filename-modifiers :kill-region-without-appending :save-column)) @@ -10,7 +10,7 @@ (defvar *previous-cwd* nil) -(defun change-directory (new-directory) +(defun change-directory* (new-directory) (check-type new-directory (or string pathname)) (let* ((previous-directory (uiop:getcwd)) (new-directory (cond diff --git a/flake.lock b/flake.lock new file mode 100644 index 000000000..a008058bf --- /dev/null +++ b/flake.lock @@ -0,0 +1,75 @@ +{ + "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1730504689, + "narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "506278e768c2a08bec68eb62932193e341f55c90", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1730785428, + "narHash": "sha256-Zwl8YgTVJTEum+L+0zVAWvXAGbWAuXHax3KzuejaDyo=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "4aa36568d413aca0ea84a1684d2d46f55dbabad7", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1730504152, + "narHash": "sha256-lXvH/vOfb4aGYyvFmZK/HlsNsr/0CVWlwYvo2rxJk3s=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/cc2f28000298e1269cea6612cd06ec9979dd5d7f.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/cc2f28000298e1269cea6612cd06ec9979dd5d7f.tar.gz" + } + }, + "root": { + "inputs": { + "flake-compat": "flake-compat", + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 000000000..c663b2340 --- /dev/null +++ b/flake.nix @@ -0,0 +1,167 @@ +{ + description = "lem"; + + inputs = { + flake-compat = { + url = "github:edolstra/flake-compat"; + flake = false; + }; + flake-parts.url = "github:hercules-ci/flake-parts"; + nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; + }; + + # cribbing a lot from https://github.com/dariof4/lem-flake + # and from https://github.com/eriedaberrie/my-nix-packages + outputs = inputs: + inputs.flake-parts.lib.mkFlake { inherit inputs; } { + systems = + [ "aarch64-darwin" "aarch64-linux" "x86_64-darwin" "x86_64-linux" ]; + perSystem = { self', pkgs, lib, ... }: + let + lem = pkgs.sbcl.buildASDFSystem rec { + pname = "lem"; + version = "unstable"; + systems = [ "lem-fake-interface" ]; + src = ./.; + + nativeBuildInputs = with pkgs; [ + autoconf + automake + libffi + libtool + makeBinaryWrapper + pkg-config + ]; + + nativeLibs = with pkgs; [ libffi openssl ]; + + qlBundleLibs = pkgs.stdenvNoCC.mkDerivation { + pname = "${pname}-qlot-bundle"; + inherit src version; + + nativeBuildInputs = with pkgs; [ + sbcl.pkgs.qlot-cli + which + git + cacert + ]; + + installPhase = '' + runHook preInstall + + export HOME=$(mktemp -d) + qlot install --jobs $NIX_BUILD_CORES --no-deps --no-color + qlot bundle --no-color + + # Unnecessary and also platform-dependent file + rm .bundle-libs/bundle-info.sexp + + # Remove vendored .so files + find .bundle-libs \ + -type f '(' -name '*.so' -o -name '*.dll' ')' -exec rm '{}' ';' + + cp -r .bundle-libs $out + + runHook postInstall + ''; + + dontBuild = true; + dontFixup = true; + }; + + configurePhase = '' + runHook preConfigure + mkdir -p $out/share/lem + pushd $out/share/lem + cp -r $qlBundleLibs .bundle-libs + chmod -R +w .bundle-libs + + # build async-process native part + pushd .bundle-libs/software/async-process-* + chmod +x bootstrap + ./bootstrap + popd + + # nixpkgs patch to fix cffi build on darwin + pushd .bundle-libs/software/cffi-* + patch -p1 <${inputs.nixpkgs}/pkgs/development/lisp-modules/patches/cffi-libffi-darwin-ffi-h.patch + popd + popd + + source ${inputs.nixpkgs}/pkgs/development/lisp-modules/setup-hook.sh + buildAsdfPath + + runHook postConfigure + ''; + + buildScript = pkgs.writeText "build-lem.lisp" '' + (defpackage :nix-cl-user + (:use :cl)) + + (in-package :nix-cl-user) + (load "${lem.asdfFasl}/asdf.${lem.faslExt}") + + ;; Avoid writing to the global fasl cache + (asdf:initialize-output-translations + '(:output-translations :disable-cache + :inherit-configuration)) + + (defvar *systems* (uiop:split-string (uiop:getenv "systems"))) + (defvar *out-path* (uiop:getenv "out")) + (defvar *share-path* (concatenate 'string + *out-path* + "/share/lem")) + (defvar *bundle-path* (concatenate 'string + *share-path* + "/.bundle-libs/bundle.lisp")) + + ;; Initial load + (let ((asdf:*system-definition-search-functions* + (copy-list asdf:*system-definition-search-functions*))) + (load *bundle-path*) + (loop :for system :in *systems* + :do (asdf:load-system system))) + + ;; Load the bundle on every startup + (uiop:register-image-restore-hook + (lambda () + (load *bundle-path*)) + nil) + + (setf uiop:*image-entry-point* #'lem:main) + (uiop:dump-image + "lem" + :executable t + :compression t) + ''; + installPhase = '' + runHook preInstall + + mkdir -p $out/bin $out/share/lem + mv lem $out/bin + wrapProgram $out/bin/lem \ + --prefix LD_LIBRARY_PATH : "$LD_LIBRARY_PATH" \ + --prefix DYLD_LIBRARY_PATH : "$DYLD_LIBRARY_PATH" + + cp -r . $out/share/lem + + runHook postInstall + ''; + }; + in { + packages.lem-ncurses = lem.overrideLispAttrs (o: { + pname = "lem-ncurses"; + systems = [ "lem-ncurses" ]; + nativeLibs = with pkgs; o.nativeLibs ++ [ ncurses ]; + }); + packages.lem-sdl2 = lem.overrideLispAttrs (o: { + pname = "lem-sdl2"; + systems = [ "lem-sdl2" ]; + nativeLibs = with pkgs; + o.nativeLibs ++ [ SDL2 SDL2_ttf SDL2_image ]; + }); + + packages.default = self'.packages.lem-ncurses; + }; + }; +} diff --git a/frontends/ncurses/drawing-object.lisp b/frontends/ncurses/drawing-object.lisp index 476292325..b0e92122d 100644 --- a/frontends/ncurses/drawing-object.lisp +++ b/frontends/ncurses/drawing-object.lisp @@ -20,7 +20,7 @@ 0) (defmethod object-width ((drawing-object line-end-object)) - 0) + (lem-core:string-width (text-object-string drawing-object))) (defmethod object-width ((drawing-object image-object)) 0) diff --git a/frontends/ncurses/mainloop.lisp b/frontends/ncurses/mainloop.lisp index 436a0eec5..3451f44a3 100644 --- a/frontends/ncurses/mainloop.lisp +++ b/frontends/ncurses/mainloop.lisp @@ -14,7 +14,7 @@ (loop (handler-case (progn - (unless (bt:thread-alive-p editor-thread) (return)) + (unless (bt2:thread-alive-p editor-thread) (return)) (let ((event (lem-ncurses/input:get-event))) (if (eq event :abort) (send-abort-event editor-thread nil) @@ -27,7 +27,7 @@ (defun invoke (function) (let ((result nil) - (input-thread (bt:current-thread))) + (input-thread (bt2:current-thread))) (unwind-protect (when (lem-ncurses/term:term-init) (let ((*standard-output* (make-broadcast-stream)) @@ -37,7 +37,7 @@ (funcall function nil (lambda (report) - (bt:interrupt-thread + (bt2:interrupt-thread input-thread (lambda () (error 'exit :value report))))))) (setf result (input-loop editor-thread))))) diff --git a/frontends/ncurses/render.lisp b/frontends/ncurses/render.lisp index 980997967..e67f4387b 100644 --- a/frontends/ncurses/render.lisp +++ b/frontends/ncurses/render.lisp @@ -21,18 +21,21 @@ (let ((string (text-object-string object)) (attribute (text-object-attribute object))) (when (and attribute (lem:cursor-attribute-p attribute)) - (lem-ncurses/view:set-last-print-cursor view x y)) + (lem-ncurses/view:set-last-print-cursor view x y) + (lem:set-attribute-foreground + attribute + (lem:color-to-hex-string (lem-if:get-background-color (lem:implementation))))) (print-string scrwin x y string attribute))) (defmethod draw-object ((object eol-cursor-object) x y view scrwin) - (lem-ncurses/view:set-last-print-cursor view x y) + (when (eol-cursor-object-true-cursor-p object) + (lem-ncurses/view:set-last-print-cursor view x y)) (print-string scrwin x y " " - (lem:make-attribute :foreground - (lem:color-to-hex-string (eol-cursor-object-color object))))) + (lem:make-attribute :foreground (lem:color-to-hex-string (eol-cursor-object-color object))))) (defmethod draw-object ((object extend-to-eol-object) x y view scrwin) (let ((width (lem-if:view-width (lem:implementation) view))) diff --git a/frontends/ncurses/term.lisp b/frontends/ncurses/term.lisp index 64a49f3b0..f78ef8caf 100644 --- a/frontends/ncurses/term.lisp +++ b/frontends/ncurses/term.lisp @@ -498,6 +498,7 @@ (charms/ll:delscreen charms/ll:*stdscr*)) (defun update-cursor-shape (cursor-type) + (check-type cursor-type lem:cursor-type) (uiop:run-program `("printf" ,(format nil "~C[~D q" #\Esc diff --git a/frontends/pdcurses/ncurses-pdcurseswin32.lisp b/frontends/pdcurses/ncurses-pdcurseswin32.lisp index efb5d5239..396131f0f 100644 --- a/frontends/pdcurses/ncurses-pdcurseswin32.lisp +++ b/frontends/pdcurses/ncurses-pdcurseswin32.lisp @@ -294,15 +294,15 @@ ;; for resizing display (defkeycode "[resize]" #x222) (let ((resize-delay-counter 0) - (lock (bt:make-lock))) + (lock (bt2:make-lock))) (defun now-resizing () - (bt:with-lock-held (lock) + (bt2:with-lock-held (lock) resize-delay-counter)) (defun (setf now-resizing) (v) - (bt:with-lock-held (lock) + (bt2:with-lock-held (lock) (setf resize-delay-counter v))) (defun now-resizing-countdown () - (bt:with-lock-held (lock) + (bt2:with-lock-held (lock) (decf resize-delay-counter)))) (defvar *min-cols* 5) (defvar *min-lines* 3) @@ -709,7 +709,7 @@ (loop (handler-case (progn - (unless (bt:thread-alive-p editor-thread) (return)) + (unless (bt2:thread-alive-p editor-thread) (return)) (let ((event (get-event))) (case event ;; retry is necessary to exit lem normally diff --git a/frontends/sdl2/display.lisp b/frontends/sdl2/display.lisp index e9595d8b9..5b2f2132b 100644 --- a/frontends/sdl2/display.lisp +++ b/frontends/sdl2/display.lisp @@ -7,6 +7,7 @@ (:export :with-display :display :current-display + :display-cursor-type :display-latin-font :display-cjk-normal-font :display-redraw-at-least-once-p @@ -55,8 +56,11 @@ `(call-with-display (lambda (,display) ,@body))) (defclass display () - ((mutex :initform (bt:make-lock "lem-sdl2 display mutex") + ((mutex :initform (bt2:make-lock :name "lem-sdl2 display mutex") :reader display-mutex) + (cursor-type :initform :box + :accessor display-cursor-type + :type lem:cursor-type) (font-config :initarg :font-config :accessor display-font-config) (font :initarg :font @@ -108,7 +112,7 @@ (defun call-with-renderer (display function) (sdl2:in-main-thread () - (bt:with-recursive-lock-held ((display-mutex display)) + (bt2:with-recursive-lock-held ((display-mutex display)) (funcall function)))) (defmacro with-renderer ((display) &body body) @@ -174,7 +178,7 @@ (nth-value 1 (sdl2:get-window-size (display-window display)))) (defmethod update-texture ((display display)) - (bt:with-lock-held ((display-mutex display)) + (bt2:with-lock-held ((display-mutex display)) (sdl2:destroy-texture (display-texture display)) (setf (display-texture display) (utils:create-texture (display-renderer display) diff --git a/frontends/sdl2/drawing.lisp b/frontends/sdl2/drawing.lisp index cc1486459..de69e458e 100644 --- a/frontends/sdl2/drawing.lisp +++ b/frontends/sdl2/drawing.lisp @@ -15,6 +15,9 @@ (defun make-text-surface (display string attribute type) (cffi:with-foreign-string (c-string string) (let ((foreground (lem-core:attribute-foreground-with-reverse attribute))) + (when (lem:cursor-attribute-p attribute) + (when (eq :box (display:display-cursor-type display)) + (setf foreground (lem-if:get-background-color (lem:implementation))))) (sdl2-ttf:render-utf8-blended (or (lem-core:attribute-font attribute) (display:get-display-font display @@ -137,6 +140,20 @@ (defmethod draw-object ((drawing-object void-object) x bottom-y display view) 0) +(defun draw-rect (display x y width height color) + (sdl2:with-rects ((rect x y width height)) + (display:set-render-color display color) + (sdl2:render-fill-rect (display:display-renderer display) rect))) + +(defun draw-cursor (display x y surface-width surface-height background) + (ecase (display:display-cursor-type display) + (:box + (draw-rect display x y surface-width surface-height background)) + (:bar + (draw-rect display x y 1 surface-height background)) + (:underline + (draw-rect display x (+ y surface-height -1) surface-width 1 background)))) + (defmethod draw-object ((drawing-object text-object) x bottom-y display view) (let* ((surface-width (object-width drawing-object display)) (surface-height (object-height drawing-object display)) @@ -146,11 +163,11 @@ (display:display-renderer display) (get-surface drawing-object display))) (y (- bottom-y surface-height))) - (when (and attribute (lem-core:cursor-attribute-p attribute)) - (lem-sdl2/view:set-cursor-position view x y)) - (sdl2:with-rects ((rect x y surface-width surface-height)) - (display:set-render-color display background) - (sdl2:render-fill-rect (display:display-renderer display) rect)) + (cond ((and attribute (lem-core:cursor-attribute-p attribute)) + (lem-sdl2/view:set-cursor-position view x y) + (draw-cursor display x y surface-width surface-height background)) + (t + (draw-rect display x y surface-width surface-height background))) (lem-sdl2/utils:render-texture (display:display-renderer display) texture x @@ -176,11 +193,12 @@ (display:set-render-color display (eol-cursor-object-color drawing-object)) (let ((y (- bottom-y (object-height drawing-object display)))) (lem-sdl2/view:set-cursor-position view x y) - (sdl2:with-rects ((rect x - y - (display:display-char-width display) - (object-height drawing-object display))) - (sdl2:render-fill-rect (display:display-renderer display) rect))) + (draw-cursor display + x + y + (display:display-char-width display) + (object-height drawing-object display) + (eol-cursor-object-color drawing-object))) (object-width drawing-object display)) (defmethod draw-object ((drawing-object extend-to-eol-object) x bottom-y display view) diff --git a/frontends/sdl2/keyboard.lisp b/frontends/sdl2/keyboard.lisp index fbf495176..485b32d30 100644 --- a/frontends/sdl2/keyboard.lisp +++ b/frontends/sdl2/keyboard.lisp @@ -139,17 +139,6 @@ (defmethod handle-key-up ((platform lem-sdl2/platform:linux) key-event) (handle-key-up-unix key-event)) -;; freebsd -(defmethod handle-text-input ((platform lem-sdl2/platform:freebsd) text) - (handle-text-input-unix text)) - -(defmethod handle-key-down ((platform lem-sdl2/platform:freebsd) key-event) - (handle-key-down-unix key-event)) - -(defmethod handle-key-up ((platform lem-sdl2/platform:freebsd) key-event) - (handle-key-up-unix key-event)) - -;; shared freebsd, linux (defun handle-text-input-unix (text) (when (modifier-is-accept-text-input-p *modifier*) (loop :for c :across text @@ -271,7 +260,7 @@ (loop :for c :across text :do (let ((key (make-key :ctrl (modifier-ctrl *modifier*) :meta (modifier-meta *modifier*) - ;; :super (modifier-super *modifier*) + :super (modifier-super *modifier*) :shift nil :sym (convert-to-sym (char-code c))))) (send-key-event key))))) @@ -286,11 +275,12 @@ (or (not text-input-p) (modifier-ctrl modifier) (modifier-meta modifier) + (modifier-super modifier) (< 256 code))) (let ((key (make-key-with-shift-careful :shift (modifier-shift modifier) :ctrl (modifier-ctrl modifier) :meta (modifier-meta modifier) - ;; :super (modifier-super modifier) + :super (modifier-super modifier) :sym sym))) (send-key-event key))))))) diff --git a/frontends/sdl2/main.lisp b/frontends/sdl2/main.lisp index 3f6d1c157..e3407c227 100644 --- a/frontends/sdl2/main.lisp +++ b/frontends/sdl2/main.lisp @@ -152,15 +152,15 @@ (sdl2:with-window (window :title "Lem" :w window-width :h window-height - :flags '(:shown :resizable #+darwin :allow-highdpi)) + :flags '(:shown :resizable :allow-highdpi)) (init-application-icon window) (sdl2:with-renderer (renderer window :index -1 :flags '(:accelerated)) - (let* (#+darwin (renderer-size (multiple-value-list + (let* ((renderer-size (multiple-value-list (sdl2:get-renderer-output-size renderer))) - #+darwin (renderer-width (first renderer-size)) - #+darwin(renderer-height (second renderer-size)) - (scale-x #-darwin 1 #+darwin (/ renderer-width window-width)) - (scale-y #-darwin 1 #+darwin (/ renderer-height window-height)) + (renderer-width (first renderer-size)) + (renderer-height (second renderer-size)) + (scale-x (/ renderer-width window-width)) + (scale-y (/ renderer-height window-height)) (texture (lem-sdl2/utils:create-texture renderer (* scale-x window-width) (* scale-y window-height))) @@ -174,7 +174,6 @@ :char-height (font-char-height font) :scale (list scale-x scale-y)))) (setf (display:current-display) display) - #+darwin (display:adapt-high-dpi-font-size display) (sdl2:start-text-input) (funcall function) @@ -245,6 +244,11 @@ (setf (display:display-background-color display) (lem:parse-color color))))) +(defmethod lem-if:update-cursor-shape ((implementation sdl2) cursor-type) + (with-debug ("lem-if:update-cursor-type") + (display:with-display (display) + (setf (display:display-cursor-type display) cursor-type)))) + (defmethod lem-if:display-width ((implementation sdl2)) (with-debug ("lem-if:display-width") (display:with-display (display) diff --git a/frontends/sdl2/platform.lisp b/frontends/sdl2/platform.lisp index ad251e971..0d087b226 100644 --- a/frontends/sdl2/platform.lisp +++ b/frontends/sdl2/platform.lisp @@ -3,7 +3,6 @@ (:export :platform :linux :mac - :freebsd :windows :get-platform)) (in-package :lem-sdl2/platform) @@ -12,7 +11,6 @@ (defclass linux (platform) ()) (defclass windows (platform) ()) (defclass mac (platform) ()) -(defclass freebsd (platform) ()) (defvar *platform* nil) @@ -21,13 +19,9 @@ (setf *platform* (let ((platform-name (sdl2:platform))) (alexandria:switch (platform-name :test #'equal) - ("Linux" - (make-instance 'linux)) - ("Mac OS X" - (make-instance 'mac)) - ("FreeBSD" - (make-instance 'freebsd)) ("Windows" (make-instance 'windows)) + ("Mac OS X" + (make-instance 'mac)) (otherwise - (error "unsupported platform: ~A" (sdl2:platform)))))))) + (make-instance 'linux))))))) diff --git a/frontends/server/config.lisp b/frontends/server/config.lisp new file mode 100644 index 000000000..3a80f6ef5 --- /dev/null +++ b/frontends/server/config.lisp @@ -0,0 +1,3 @@ +(in-package :cl-user) + +(setf lem-markdown-mode/internal::*view-type* :html-buffer) diff --git a/frontends/server/frontend/.gitignore b/frontends/server/frontend/.gitignore new file mode 100644 index 000000000..a9b26d1fe --- /dev/null +++ b/frontends/server/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +#dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/frontends/server/frontend/dist/assets/index-JI7myQ4_.js b/frontends/server/frontend/dist/assets/index-JI7myQ4_.js new file mode 100644 index 000000000..8f2fe72c7 --- /dev/null +++ b/frontends/server/frontend/dist/assets/index-JI7myQ4_.js @@ -0,0 +1 @@ +(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))i(o);new MutationObserver(o=>{for(const n of o)if(n.type==="childList")for(const s of n.addedNodes)s.tagName==="LINK"&&s.rel==="modulepreload"&&i(s)}).observe(document,{childList:!0,subtree:!0});function t(o){const n={};return o.integrity&&(n.integrity=o.integrity),o.referrerPolicy&&(n.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?n.credentials="include":o.crossOrigin==="anonymous"?n.credentials="omit":n.credentials="same-origin",n}function i(o){if(o.ep)return;o.ep=!0;const n=t(o);fetch(o.href,n)}})();var commonjsGlobal=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{},dist={},client={},models={};(function(r){var e=commonjsGlobal&&commonjsGlobal.__extends||function(){var d=function(f,p){return d=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(N,v){N.__proto__=v}||function(N,v){for(var y in v)Object.prototype.hasOwnProperty.call(v,y)&&(N[y]=v[y])},d(f,p)};return function(f,p){if(typeof p!="function"&&p!==null)throw new TypeError("Class extends value "+String(p)+" is not a constructor or null");d(f,p);function N(){this.constructor=f}f.prototype=p===null?Object.create(p):(N.prototype=p.prototype,new N)}}();Object.defineProperty(r,"__esModule",{value:!0}),r.createJSONRPCNotification=r.createJSONRPCRequest=r.createJSONRPCSuccessResponse=r.createJSONRPCErrorResponse=r.JSONRPCErrorCode=r.JSONRPCErrorException=r.isJSONRPCResponses=r.isJSONRPCResponse=r.isJSONRPCRequests=r.isJSONRPCRequest=r.isJSONRPCID=r.JSONRPC=void 0,r.JSONRPC="2.0";var t=function(d){return typeof d=="string"||typeof d=="number"||d===null};r.isJSONRPCID=t;var i=function(d){return d.jsonrpc===r.JSONRPC&&d.method!==void 0&&d.result===void 0&&d.error===void 0};r.isJSONRPCRequest=i;var o=function(d){return Array.isArray(d)&&d.every(r.isJSONRPCRequest)};r.isJSONRPCRequests=o;var n=function(d){return d.jsonrpc===r.JSONRPC&&d.id!==void 0&&(d.result!==void 0||d.error!==void 0)};r.isJSONRPCResponse=n;var s=function(d){return Array.isArray(d)&&d.every(r.isJSONRPCResponse)};r.isJSONRPCResponses=s;var l=function(d,f,p){var N={code:d,message:f};return p!=null&&(N.data=p),N},c=function(d){e(f,d);function f(p,N,v){var y=d.call(this,p)||this;return Object.setPrototypeOf(y,f.prototype),y.code=N,y.data=v,y}return f.prototype.toObject=function(){return l(this.code,this.message,this.data)},f}(Error);r.JSONRPCErrorException=c,function(d){d[d.ParseError=-32700]="ParseError",d[d.InvalidRequest=-32600]="InvalidRequest",d[d.MethodNotFound=-32601]="MethodNotFound",d[d.InvalidParams=-32602]="InvalidParams",d[d.InternalError=-32603]="InternalError"}(r.JSONRPCErrorCode||(r.JSONRPCErrorCode={}));var a=function(d,f,p,N){return{jsonrpc:r.JSONRPC,id:d,error:l(f,p,N)}};r.createJSONRPCErrorResponse=a;var u=function(d,f){return{jsonrpc:r.JSONRPC,id:d,result:f??null}};r.createJSONRPCSuccessResponse=u;var h=function(d,f,p){return{jsonrpc:r.JSONRPC,id:d,method:f,params:p}};r.createJSONRPCRequest=h;var m=function(d,f){return{jsonrpc:r.JSONRPC,method:d,params:f}};r.createJSONRPCNotification=m})(models);var internal={};Object.defineProperty(internal,"__esModule",{value:!0});internal.DefaultErrorCode=void 0;internal.DefaultErrorCode=0;var __awaiter$2=commonjsGlobal&&commonjsGlobal.__awaiter||function(r,e,t,i){function o(n){return n instanceof t?n:new t(function(s){s(n)})}return new(t||(t=Promise))(function(n,s){function l(u){try{a(i.next(u))}catch(h){s(h)}}function c(u){try{a(i.throw(u))}catch(h){s(h)}}function a(u){u.done?n(u.value):o(u.value).then(l,c)}a((i=i.apply(r,e||[])).next())})},__generator$2=commonjsGlobal&&commonjsGlobal.__generator||function(r,e){var t={label:0,sent:function(){if(n[0]&1)throw n[1];return n[1]},trys:[],ops:[]},i,o,n,s;return s={next:l(0),throw:l(1),return:l(2)},typeof Symbol=="function"&&(s[Symbol.iterator]=function(){return this}),s;function l(a){return function(u){return c([a,u])}}function c(a){if(i)throw new TypeError("Generator is already executing.");for(;s&&(s=0,a[0]&&(t=0)),t;)try{if(i=1,o&&(n=a[0]&2?o.return:a[0]?o.throw||((n=o.return)&&n.call(o),0):o.next)&&!(n=n.call(o,a[1])).done)return n;switch(o=0,n&&(a=[a[0]&2,n.value]),a[0]){case 0:case 1:n=a;break;case 4:return t.label++,{value:a[1],done:!1};case 5:t.label++,o=a[1],a=[0];continue;case 7:a=t.ops.pop(),t.trys.pop();continue;default:if(n=t.trys,!(n=n.length>0&&n[n.length-1])&&(a[0]===6||a[0]===2)){t=0;continue}if(a[0]===3&&(!n||a[1]>n[0]&&a[1]0&&n[n.length-1])&&(a[0]===6||a[0]===2)){t=0;continue}if(a[0]===3&&(!n||a[1]>n[0]&&a[1]0&&n[n.length-1])&&(a[0]===6||a[0]===2)){t=0;continue}if(a[0]===3&&(!n||a[1]>n[0]&&a[1]{const[t,i,o]=e;this.requestInternal(t,i,o)}),this.messageQueue=[]}request(e,t,i){this.webSocket.readyState===WebSocket.OPEN?this.requestInternal(e,t,i):this.messageQueue.push([e,t,i])}notify(e,t){switch(this.webSocket.readyState){case WebSocket.OPEN:this.serverAndClient.notify(e,t);break}}connect(e){this.closed||(console.log("connect",this.url),this.webSocket=new WebSocket(this.url),this.serverAndClient||(this.serverAndClient=new dist.JSONRPCServerAndClient(new dist.JSONRPCServer,new dist.JSONRPCClient(t=>{try{return this.webSocket.send(JSON.stringify(t)),Promise.resolve()}catch(i){return Promise.reject(i)}}))),this.webSocket.onmessage=t=>{this.serverAndClient.receiveAndSend(JSON.parse(t.data.toString()))},this.webSocket.onopen=()=>{console.log("WebSocket connection established"),this.connectionEstablished=!0,this.onConnected&&this.onConnected(),this.requestMessageQueue()},this.webSocket.onclose=t=>{console.error("WebScoket closed",t),this.serverAndClient.rejectAllPendingRequests(`Connection is closed (${t.reason}).`),this.connectionEstablished&&this.onClosed(),this.timerId=setTimeout(()=>{this.connect()},3e3)},this.webSocket.onerror=t=>{console.error("WebSocket error:",t),this.webSocket.close()})}}const modifierKeys=["Shift","Control","Alt","Meta"],convertKeyTable={Enter:"Return",ArrowRight:"Right",ArrowLeft:"Left",ArrowUp:"Up",ArrowDown:"Down","¡":"1","™":"2","£":"3","¢":"4","∞":"5","§":"6","¶":"7","•":"8",ª:"9",º:"0","–":"-","≠":"=","“":"[","‘":"]","«":"\\","…":";",æ:"'","≤":",","≥":".","÷":"/","⁄":"!","€":"@","‹":"#","›":"$",fi:"%",fl:"^","‡":"&","°":"*","·":"(","‚":")","—":"_","±":"+","”":"{","’":"}","»":"|",Ú:":",Æ:'"',"¯":"<","˘":">","¿":"?",œ:"q","∑":"w","´":"e","®":"r","†":"t","¥":"y","¨":"u","ˆ":"i",ø:"o",π:"p",å:"a",ß:"s","∂":"d",ƒ:"f","©":"g","˙":"h","∆":"j","˚":"k","¬":"l",Ω:"z","≈":"x",ç:"c","√":"v","∫":"b","˜":"n",µ:"m",Œ:"Q","„":"W","´":"E","‰":"R","ˇ":"T",Á:"Y","¨":"U","ˆ":"I",Ø:"O","∏":"P",Å:"A",Í:"S",Î:"D",Ï:"F","˝":"G",Ó:"H",Ô:"J","":"K",Ò:"L","¸":"Z","˛":"X",Ç:"C","◊":"V",ı:"B","˜":"N",Â:"M"};function getKey(r){return r.altKey?convertKeyTable[r.key]||(r.code.startsWith("Key")?r.code[3].toLowerCase():null)||r.key:convertKeyTable[r.key]||r.key}function convertKeyEvent(r){return modifierKeys.indexOf(r.key)!==-1?null:{key:getKey(r),ctrl:r.ctrlKey,meta:r.altKey,super:r.metaKey,shift:r.shiftKey}}var defs=[[0,31,"N"],[32,126,"Na"],[127,160,"N"],[161,161,"A"],[162,163,"Na"],[164,164,"A"],[165,166,"Na"],[167,168,"A"],[169,169,"N"],[170,170,"A"],[171,171,"N"],[172,172,"Na"],[173,174,"A"],[175,175,"Na"],[176,180,"A"],[181,181,"N"],[182,186,"A"],[187,187,"N"],[188,191,"A"],[192,197,"N"],[198,198,"A"],[199,207,"N"],[208,208,"A"],[209,214,"N"],[215,216,"A"],[217,221,"N"],[222,225,"A"],[226,229,"N"],[230,230,"A"],[231,231,"N"],[232,234,"A"],[235,235,"N"],[236,237,"A"],[238,239,"N"],[240,240,"A"],[241,241,"N"],[242,243,"A"],[244,246,"N"],[247,250,"A"],[251,251,"N"],[252,252,"A"],[253,253,"N"],[254,254,"A"],[255,256,"N"],[257,257,"A"],[258,272,"N"],[273,273,"A"],[274,274,"N"],[275,275,"A"],[276,282,"N"],[283,283,"A"],[284,293,"N"],[294,295,"A"],[296,298,"N"],[299,299,"A"],[300,304,"N"],[305,307,"A"],[308,311,"N"],[312,312,"A"],[313,318,"N"],[319,322,"A"],[323,323,"N"],[324,324,"A"],[325,327,"N"],[328,331,"A"],[332,332,"N"],[333,333,"A"],[334,337,"N"],[338,339,"A"],[340,357,"N"],[358,359,"A"],[360,362,"N"],[363,363,"A"],[364,461,"N"],[462,462,"A"],[463,463,"N"],[464,464,"A"],[465,465,"N"],[466,466,"A"],[467,467,"N"],[468,468,"A"],[469,469,"N"],[470,470,"A"],[471,471,"N"],[472,472,"A"],[473,473,"N"],[474,474,"A"],[475,475,"N"],[476,476,"A"],[477,592,"N"],[593,593,"A"],[594,608,"N"],[609,609,"A"],[610,707,"N"],[708,708,"A"],[709,710,"N"],[711,711,"A"],[712,712,"N"],[713,715,"A"],[716,716,"N"],[717,717,"A"],[718,719,"N"],[720,720,"A"],[721,727,"N"],[728,731,"A"],[732,732,"N"],[733,733,"A"],[734,734,"N"],[735,735,"A"],[736,767,"N"],[768,879,"A"],[880,912,"N"],[913,929,"A"],[930,930,"N"],[931,937,"A"],[938,944,"N"],[945,961,"A"],[962,962,"N"],[963,969,"A"],[970,1024,"N"],[1025,1025,"A"],[1026,1039,"N"],[1040,1103,"A"],[1104,1104,"N"],[1105,1105,"A"],[1106,4351,"N"],[4352,4447,"W"],[4448,8207,"N"],[8208,8208,"A"],[8209,8210,"N"],[8211,8214,"A"],[8215,8215,"N"],[8216,8217,"A"],[8218,8219,"N"],[8220,8221,"A"],[8222,8223,"N"],[8224,8226,"A"],[8227,8227,"N"],[8228,8231,"A"],[8232,8239,"N"],[8240,8240,"A"],[8241,8241,"N"],[8242,8243,"A"],[8244,8244,"N"],[8245,8245,"A"],[8246,8250,"N"],[8251,8251,"A"],[8252,8253,"N"],[8254,8254,"A"],[8255,8307,"N"],[8308,8308,"A"],[8309,8318,"N"],[8319,8319,"A"],[8320,8320,"N"],[8321,8324,"A"],[8325,8360,"N"],[8361,8361,"H"],[8362,8363,"N"],[8364,8364,"A"],[8365,8450,"N"],[8451,8451,"A"],[8452,8452,"N"],[8453,8453,"A"],[8454,8456,"N"],[8457,8457,"A"],[8458,8466,"N"],[8467,8467,"A"],[8468,8469,"N"],[8470,8470,"A"],[8471,8480,"N"],[8481,8482,"A"],[8483,8485,"N"],[8486,8486,"A"],[8487,8490,"N"],[8491,8491,"A"],[8492,8530,"N"],[8531,8532,"A"],[8533,8538,"N"],[8539,8542,"A"],[8543,8543,"N"],[8544,8555,"A"],[8556,8559,"N"],[8560,8569,"A"],[8570,8584,"N"],[8585,8585,"A"],[8586,8591,"N"],[8592,8601,"A"],[8602,8631,"N"],[8632,8633,"A"],[8634,8657,"N"],[8658,8658,"A"],[8659,8659,"N"],[8660,8660,"A"],[8661,8678,"N"],[8679,8679,"A"],[8680,8703,"N"],[8704,8704,"A"],[8705,8705,"N"],[8706,8707,"A"],[8708,8710,"N"],[8711,8712,"A"],[8713,8714,"N"],[8715,8715,"A"],[8716,8718,"N"],[8719,8719,"A"],[8720,8720,"N"],[8721,8721,"A"],[8722,8724,"N"],[8725,8725,"A"],[8726,8729,"N"],[8730,8730,"A"],[8731,8732,"N"],[8733,8736,"A"],[8737,8738,"N"],[8739,8739,"A"],[8740,8740,"N"],[8741,8741,"A"],[8742,8742,"N"],[8743,8748,"A"],[8749,8749,"N"],[8750,8750,"A"],[8751,8755,"N"],[8756,8759,"A"],[8760,8763,"N"],[8764,8765,"A"],[8766,8775,"N"],[8776,8776,"A"],[8777,8779,"N"],[8780,8780,"A"],[8781,8785,"N"],[8786,8786,"A"],[8787,8799,"N"],[8800,8801,"A"],[8802,8803,"N"],[8804,8807,"A"],[8808,8809,"N"],[8810,8811,"A"],[8812,8813,"N"],[8814,8815,"A"],[8816,8833,"N"],[8834,8835,"A"],[8836,8837,"N"],[8838,8839,"A"],[8840,8852,"N"],[8853,8853,"A"],[8854,8856,"N"],[8857,8857,"A"],[8858,8868,"N"],[8869,8869,"A"],[8870,8894,"N"],[8895,8895,"A"],[8896,8977,"N"],[8978,8978,"A"],[8979,8985,"N"],[8986,8987,"W"],[8988,9e3,"N"],[9001,9002,"W"],[9003,9192,"N"],[9193,9196,"W"],[9197,9199,"N"],[9200,9200,"W"],[9201,9202,"N"],[9203,9203,"W"],[9204,9311,"N"],[9312,9449,"A"],[9450,9450,"N"],[9451,9547,"A"],[9548,9551,"N"],[9552,9587,"A"],[9588,9599,"N"],[9600,9615,"A"],[9616,9617,"N"],[9618,9621,"A"],[9622,9631,"N"],[9632,9633,"A"],[9634,9634,"N"],[9635,9641,"A"],[9642,9649,"N"],[9650,9651,"A"],[9652,9653,"N"],[9654,9655,"A"],[9656,9659,"N"],[9660,9661,"A"],[9662,9663,"N"],[9664,9665,"A"],[9666,9669,"N"],[9670,9672,"A"],[9673,9674,"N"],[9675,9675,"A"],[9676,9677,"N"],[9678,9681,"A"],[9682,9697,"N"],[9698,9701,"A"],[9702,9710,"N"],[9711,9711,"A"],[9712,9724,"N"],[9725,9726,"W"],[9727,9732,"N"],[9733,9734,"A"],[9735,9736,"N"],[9737,9737,"A"],[9738,9741,"N"],[9742,9743,"A"],[9744,9747,"N"],[9748,9749,"W"],[9750,9755,"N"],[9756,9756,"A"],[9757,9757,"N"],[9758,9758,"A"],[9759,9791,"N"],[9792,9792,"A"],[9793,9793,"N"],[9794,9794,"A"],[9795,9799,"N"],[9800,9811,"W"],[9812,9823,"N"],[9824,9825,"A"],[9826,9826,"N"],[9827,9829,"A"],[9830,9830,"N"],[9831,9834,"A"],[9835,9835,"N"],[9836,9837,"A"],[9838,9838,"N"],[9839,9839,"A"],[9840,9854,"N"],[9855,9855,"W"],[9856,9874,"N"],[9875,9875,"W"],[9876,9885,"N"],[9886,9887,"A"],[9888,9888,"N"],[9889,9889,"W"],[9890,9897,"N"],[9898,9899,"W"],[9900,9916,"N"],[9917,9918,"W"],[9919,9919,"A"],[9920,9923,"N"],[9924,9925,"W"],[9926,9933,"A"],[9934,9934,"W"],[9935,9939,"A"],[9940,9940,"W"],[9941,9953,"A"],[9954,9954,"N"],[9955,9955,"A"],[9956,9959,"N"],[9960,9961,"A"],[9962,9962,"W"],[9963,9969,"A"],[9970,9971,"W"],[9972,9972,"A"],[9973,9973,"W"],[9974,9977,"A"],[9978,9978,"W"],[9979,9980,"A"],[9981,9981,"W"],[9982,9983,"A"],[9984,9988,"N"],[9989,9989,"W"],[9990,9993,"N"],[9994,9995,"W"],[9996,10023,"N"],[10024,10024,"W"],[10025,10044,"N"],[10045,10045,"A"],[10046,10059,"N"],[10060,10060,"W"],[10061,10061,"N"],[10062,10062,"W"],[10063,10066,"N"],[10067,10069,"W"],[10070,10070,"N"],[10071,10071,"W"],[10072,10101,"N"],[10102,10111,"A"],[10112,10132,"N"],[10133,10135,"W"],[10136,10159,"N"],[10160,10160,"W"],[10161,10174,"N"],[10175,10175,"W"],[10176,10213,"N"],[10214,10221,"Na"],[10222,10628,"N"],[10629,10630,"Na"],[10631,11034,"N"],[11035,11036,"W"],[11037,11087,"N"],[11088,11088,"W"],[11089,11092,"N"],[11093,11093,"W"],[11094,11097,"A"],[11098,11903,"N"],[11904,11929,"W"],[11930,11930,"N"],[11931,12019,"W"],[12020,12031,"N"],[12032,12245,"W"],[12246,12271,"N"],[12272,12287,"W"],[12288,12288,"F"],[12289,12350,"W"],[12351,12352,"N"],[12353,12438,"W"],[12439,12440,"N"],[12441,12543,"W"],[12544,12548,"N"],[12549,12591,"W"],[12592,12592,"N"],[12593,12686,"W"],[12687,12687,"N"],[12688,12771,"W"],[12772,12782,"N"],[12783,12830,"W"],[12831,12831,"N"],[12832,12871,"W"],[12872,12879,"A"],[12880,19903,"W"],[19904,19967,"N"],[19968,42124,"W"],[42125,42127,"N"],[42128,42182,"W"],[42183,43359,"N"],[43360,43388,"W"],[43389,44031,"N"],[44032,55203,"W"],[55204,57343,"N"],[57344,63743,"A"],[63744,64255,"W"],[64256,65023,"N"],[65024,65039,"A"],[65040,65049,"W"],[65050,65071,"N"],[65072,65106,"W"],[65107,65107,"N"],[65108,65126,"W"],[65127,65127,"N"],[65128,65131,"W"],[65132,65280,"N"],[65281,65376,"F"],[65377,65470,"H"],[65471,65473,"N"],[65474,65479,"H"],[65480,65481,"N"],[65482,65487,"H"],[65488,65489,"N"],[65490,65495,"H"],[65496,65497,"N"],[65498,65500,"H"],[65501,65503,"N"],[65504,65510,"F"],[65511,65511,"N"],[65512,65518,"H"],[65519,65532,"N"],[65533,65533,"A"],[65534,94175,"N"],[94176,94180,"W"],[94181,94191,"N"],[94192,94193,"W"],[94194,94207,"N"],[94208,100343,"W"],[100344,100351,"N"],[100352,101589,"W"],[101590,101631,"N"],[101632,101640,"W"],[101641,110575,"N"],[110576,110579,"W"],[110580,110580,"N"],[110581,110587,"W"],[110588,110588,"N"],[110589,110590,"W"],[110591,110591,"N"],[110592,110882,"W"],[110883,110897,"N"],[110898,110898,"W"],[110899,110927,"N"],[110928,110930,"W"],[110931,110932,"N"],[110933,110933,"W"],[110934,110947,"N"],[110948,110951,"W"],[110952,110959,"N"],[110960,111355,"W"],[111356,126979,"N"],[126980,126980,"W"],[126981,127182,"N"],[127183,127183,"W"],[127184,127231,"N"],[127232,127242,"A"],[127243,127247,"N"],[127248,127277,"A"],[127278,127279,"N"],[127280,127337,"A"],[127338,127343,"N"],[127344,127373,"A"],[127374,127374,"W"],[127375,127376,"A"],[127377,127386,"W"],[127387,127404,"A"],[127405,127487,"N"],[127488,127490,"W"],[127491,127503,"N"],[127504,127547,"W"],[127548,127551,"N"],[127552,127560,"W"],[127561,127567,"N"],[127568,127569,"W"],[127570,127583,"N"],[127584,127589,"W"],[127590,127743,"N"],[127744,127776,"W"],[127777,127788,"N"],[127789,127797,"W"],[127798,127798,"N"],[127799,127868,"W"],[127869,127869,"N"],[127870,127891,"W"],[127892,127903,"N"],[127904,127946,"W"],[127947,127950,"N"],[127951,127955,"W"],[127956,127967,"N"],[127968,127984,"W"],[127985,127987,"N"],[127988,127988,"W"],[127989,127991,"N"],[127992,128062,"W"],[128063,128063,"N"],[128064,128064,"W"],[128065,128065,"N"],[128066,128252,"W"],[128253,128254,"N"],[128255,128317,"W"],[128318,128330,"N"],[128331,128334,"W"],[128335,128335,"N"],[128336,128359,"W"],[128360,128377,"N"],[128378,128378,"W"],[128379,128404,"N"],[128405,128406,"W"],[128407,128419,"N"],[128420,128420,"W"],[128421,128506,"N"],[128507,128591,"W"],[128592,128639,"N"],[128640,128709,"W"],[128710,128715,"N"],[128716,128716,"W"],[128717,128719,"N"],[128720,128722,"W"],[128723,128724,"N"],[128725,128727,"W"],[128728,128731,"N"],[128732,128735,"W"],[128736,128746,"N"],[128747,128748,"W"],[128749,128755,"N"],[128756,128764,"W"],[128765,128991,"N"],[128992,129003,"W"],[129004,129007,"N"],[129008,129008,"W"],[129009,129291,"N"],[129292,129338,"W"],[129339,129339,"N"],[129340,129349,"W"],[129350,129350,"N"],[129351,129535,"W"],[129536,129647,"N"],[129648,129660,"W"],[129661,129663,"N"],[129664,129672,"W"],[129673,129679,"N"],[129680,129725,"W"],[129726,129726,"N"],[129727,129733,"W"],[129734,129741,"N"],[129742,129755,"W"],[129756,129759,"N"],[129760,129768,"W"],[129769,129775,"N"],[129776,129784,"W"],[129785,131071,"N"],[131072,196605,"W"],[196606,196607,"N"],[196608,262141,"W"],[262142,917759,"N"],[917760,917999,"A"],[918e3,983039,"N"],[983040,1048573,"A"],[1048574,1048575,"N"],[1048576,1114109,"A"],[1114110,1114111,"N"]];function getEAWOfCodePoint(r){let e=0,t=defs.length-1;for(;e!==t;){const i=e+(t-e>>1),[o,n,s]=defs[i];if(rn)e=i+1;else return s}return defs[e][2]}function getEAW(r,e=0){const t=r.codePointAt(e);if(t!==void 0)return getEAWOfCodePoint(t)}const textOffsetY=5,isSafari=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);function isWideChar(r){switch(getEAW(r)){case"A":case"F":case"W":return!0;default:return!1}}function isMacOS(){return window.navigator.userAgent.indexOf("Mac OS X")!==-1}function computeFontSize(r){const t=document.createElement("canvas").getContext("2d");t.font=r;const i=t.measureText("W");return[Math.floor(i.width),Math.round(i.fontBoundingBoxAscent+textOffsetY+(i.emHeightDescent||0))]}function drawBlock({ctx:r,x:e,y:t,width:i,height:o,style:n}){r.fillStyle=n,r.fillRect(e,t,i,o)}function drawText({ctx:r,x:e,y:t,text:i,font:o,style:n,option:s}){t+=Math.round(textOffsetY),r.fillStyle=n,r.font=o,r.textBaseline="top";for(const l of i)isWideChar(l)?(r.fillText(l,e,t,s.fontWidth*2),e+=s.fontWidth*2):(r.fillText(l,e,t,s.fontWidth),e+=s.fontWidth)}function drawHorizontalLine({ctx:r,x:e,y:t,width:i,style:o,lineWidth:n=1}){r.strokeStyle=o,r.lineWidth=n,r.setLineDash=[],r.beginPath(),r.moveTo(e,t),r.lineTo(e+i,t),r.stroke()}class Option{constructor({fontName:e,fontSize:t}){const i=t+"px "+e;this.font=i;const[o,n]=computeFontSize(i);this.fontWidth=o,this.fontHeight=n,this.foreground="#333",this.background="#ccc"}}function getLemEditorElement(){return document.getElementById("lem-editor")}class Cursor{constructor(e,t,i){this.editor=e,this.name=t,this.color=i,this.span=document.createElement("span"),this.span.style.all="none",this.span.style.position="absolute",this.span.style.zIndex="",this.span.style.top="0",this.span.style.left="0",this.span.style.fontFamily=e.option.font,this.span.style.backgroundColor=i,this.span.style.color="white",this.span.innerHTML="",document.body.appendChild(this.span),this.timerId=null}move(e,t){const[i,o]=this.editor.getDisplayRectangle();this.span.style.visibility="visible",this.span.textContent=this.name,this.span.style.left=e+i+"px",this.span.style.top=t+o+"px",this.span.style.padding="3px 1%",this.timerId&&clearTimeout(this.timerId),this.timerId=setTimeout(()=>{this.span.style.visibility="hidden"},500)}}function addMouseEventListeners({dom:r,editor:e,isDraggable:t,draggableStyle:i}){r.addEventListener("contextmenu",s=>{s.preventDefault()});const o=(s,l)=>{s.preventDefault();const[c,a]=e.getDisplayRectangle(),u=s.clientX-c,h=s.clientY-a,m=Math.floor(u/e.option.fontWidth),d=Math.floor(h/e.option.fontHeight);e.jsonrpc.notify("input",{kind:l,value:{x:m,y:d,pixelX:u,pixelY:h,button:s.button,clicks:s.detail}})};r.addEventListener("mousedown",s=>{t&&(document.body.style.cursor=i),o(s,"mousedown")}),r.addEventListener("mouseup",s=>{t&&(document.body.style.cursor="default"),o(s,"mouseup")});let n=0;r.addEventListener("mousemove",s=>{s.preventDefault();const l=Date.now();if(l-n>50){n=l;const[c,a]=e.getDisplayRectangle(),u=s.clientX-c,h=s.clientY-a,m=Math.floor(u/e.option.fontWidth),d=Math.floor(h/e.option.fontHeight);e.jsonrpc.notify("input",{kind:"mousemove",value:{x:m,y:d,pixelX:u,pixelY:h,button:s.buttons===0?null:s.buttons-1}})}}),t&&(r.addEventListener("mouseover",()=>{document.body.style.cursor=i}),r.addEventListener("mouseout",s=>{s.buttons!==1&&(document.body.style.cursor="default")})),r.addEventListener("wheel",s=>{s.preventDefault();const[l,c]=e.getDisplayRectangle(),a=s.clientX-l,u=s.clientY-c,h=Math.floor(a/e.option.fontWidth),m=Math.floor(u/e.option.fontHeight);e.jsonrpc.notify("input",{kind:"wheel",value:{pixelX:a,pixelY:u,x:h,y:m,wheelX:-Math.round(s.deltaX*.01),wheelY:-Math.round(s.deltaY*.01)}})})}const borderOffsetX=5,borderOffsetY=10;class BaseSurface{constructor({editor:r}){this.editor=r,this.mainDOM=null,this.wrapper=null}delete(){this.wrapper?getLemEditorElement().removeChild(this.wrapper):getLemEditorElement().removeChild(this.mainDOM)}setupDOM({dom:r,isFloating:e,border:t}){this.mainDOM=r,e&&t?(this.wrapper=document.createElement("div"),this.wrapper.style.position="absolute",this.wrapper.style.padding="10px",this.wrapper.style.border="1px solid",this.wrapper.style.borderColor=this.editor.option.foreground,this.wrapper.style.backgroundColor=this.editor.option.background,this.wrapper.appendChild(r),getLemEditorElement().appendChild(this.wrapper)):getLemEditorElement().appendChild(r)}move(r,e){const[t,i]=this.editor.getDisplayRectangle(),o=Math.floor(t+r*this.editor.option.fontWidth),n=Math.floor(i+e*this.editor.option.fontHeight);this.wrapper?(this.wrapper.style.left=o-borderOffsetX+"px",this.wrapper.style.top=n-borderOffsetY+"px",this.mainDOM.style.left=borderOffsetX+"px",this.mainDOM.style.top=borderOffsetY+"px"):(this.mainDOM.style.left=o+"px",this.mainDOM.style.top=n+"px")}resize(r,e){const t=window.devicePixelRatio||1;this.mainDOM.width=r*this.editor.option.fontWidth*t,this.mainDOM.height=e*this.editor.option.fontHeight*t,this.mainDOM.style.width=r*this.editor.option.fontWidth+"px",this.mainDOM.style.height=e*this.editor.option.fontHeight+"px",this.mainDOM.getContext("2d").scale(t,t),this.wrapper&&(this.wrapper.style.width=r*this.editor.option.fontWidth+borderOffsetX*2+"px",this.wrapper.style.height=e*this.editor.option.fontHeight+borderOffsetY*2+"px")}drawBlock(r,e,t,i,o){}drawText(r,e,t,i,o){}touch(){}evalIn(code){return eval(code)}}class CanvasSurface extends BaseSurface{constructor({editor:e,view:t,x:i,y:o,width:n,height:s,styles:l,isFloating:c,border:a}){super({editor:e});const u=this.setupCanvas(l);this.setupDOM({dom:u,isFloating:c,border:a}),this.move(i,o),this.resize(n,s),this.drawingQueue=[],addMouseEventListeners({dom:u,editor:e})}setupCanvas(e){const t=document.createElement("canvas");if(t.style.position="absolute",e)for(let i in e)t.style[i]=e[i];return t}drawBlock(e,t,i,o,n){const s=this.editor.option;this.drawingQueue.push(function(l){drawBlock({ctx:l,x:e*s.fontWidth,y:t*s.fontHeight,width:i*s.fontWidth,height:o*s.fontHeight,style:n})})}drawText(e,t,i,o,n){const s=this.editor.option;this.drawingQueue.push(function(l){if(!n)drawBlock({ctx:l,x:e*s.fontWidth,y:t*s.fontHeight,width:o*s.fontWidth,height:s.fontHeight,style:s.background}),drawText({ctx:l,x:e*s.fontWidth,y:t*s.fontHeight,text:i,style:s.foreground,font:s.font,option:s});else{let{foreground:c,background:a,bold:u,reverse:h,underline:m}=n;if(c||(c=s.foreground),a||(a=s.background),h){const p=a;a=c,c=p}const d=e*s.fontWidth,f=t*s.fontHeight;drawBlock({ctx:l,x:d,y:f,width:o*s.fontWidth,height:s.fontHeight,style:a}),drawText({ctx:l,x:d,y:f,text:i,style:c,font:u?"bold "+s.font:s.font,option:s}),m&&drawHorizontalLine({ctx:l,x:d,y:f+s.fontHeight-2,width:o*s.fontWidth,style:typeof m=="string"?m:c,lineWidth:2})}})}touch(){const e=this.mainDOM.getContext("2d");for(let t of this.drawingQueue)t(e);this.drawingQueue=[]}}class HTMLSurface extends BaseSurface{constructor({editor:e,x:t,y:i,width:o,height:n,styles:s,isFloating:l,border:c,html:a}){super({editor:e});const u=document.createElement("iframe");this.setupDOM({dom:u,isFloating:l,border:c}),u.style.position="absolute",u.style.backgroundColor="white",u.setAttribute("sandbox","allow-scripts allow-same-origin"),u.srcdoc=a,this.iframe=u,this.move(t,i),this.resize(o,n)}update(e){const t=this.iframe.contentWindow.scrollY;this.iframe.srcdoc=e,this.iframe.onload=()=>{this.iframe.onload=null,this.iframe.contentWindow.scrollTo(0,t)}}evalIn(e){return this.iframe.contentWindow.eval(e)}}class VerticalBorder{constructor({x:e,y:t,height:i,option:o,editor:n}){this.option=o,this.editor=n,this.line=document.createElement("div"),this.line.style.backgroundColor=o.foreground,this.line.style.width="5px",this.line.style.height=i*o.fontHeight+"px",this.line.style.position="absolute",getLemEditorElement().appendChild(this.line),this.move(e,t),addMouseEventListeners({dom:this.line,editor:n,isDraggable:!0,draggableStyle:"col-resize"})}delete(){this.line.parentNode.removeChild(this.line)}move(e,t){const[i,o]=this.editor.getDisplayRectangle();this.line.style.left=Math.floor(i+e*this.option.fontWidth-this.option.fontWidth/2)+"px",this.line.style.top=o+t*this.option.fontHeight+"px"}resize(e){this.line.style.height=e*this.option.fontHeight+"px"}}const viewStyles={tile:()=>{},floating:r=>({boxSizing:"border-box",borderColor:r.foreground,backgroundColor:r.background})};function getViewStyle(r,e){return viewStyles[r](e)||{}}class View{constructor({id:e,x:t,y:i,width:o,height:n,useModeline:s,kind:l,type:c,content:a,border:u,option:h,editor:m}){switch(this.option=h,this.id=e,this.x=t,this.y=i,this.width=o,this.height=n,this.useModeline=s,this.kind=l,this.type=c,this.border=u,this.editor=m,this.leftsideBar=null,l){case"tile":this.mainSurface=this.makeSurface(c,a),this.leftSideBar=new VerticalBorder({x:t,y:i,height:n+(s?1:0),option:h,editor:m});break;case"floating":this.mainSurface=this.makeSurface(c,a);break}this.modelineSurface=s?this.makeModelineSurface():null}delete(){this.mainSurface.delete(),this.modelineSurface&&this.modelineSurface.delete(),this.leftSideBar&&this.leftSideBar.delete()}move(e,t){this.x=e,this.y=t,this.mainSurface.move(e,t),this.modelineSurface&&this.modelineSurface.move(e,t+this.height),this.leftSideBar&&this.leftSideBar.move(e,t)}resize(e,t){this.width=e,this.height=t,this.mainSurface.resize(e,t),this.modelineSurface&&(this.modelineSurface.move(this.x,this.y+this.height),this.modelineSurface.resize(e,1)),this.leftSideBar&&this.leftSideBar.resize(t+1)}clear(){this.mainSurface.drawBlock(0,0,this.width,this.height,this.option.background)}clearEol(e,t){this.mainSurface.drawBlock(e,t,this.width-e,1,this.option.background)}clearEob(e,t){this.mainSurface.drawBlock(e,t,this.width,this.height-t,this.option.background)}print(e,t,i,o,n){this.mainSurface.drawText(e,t,i,o,n)}printToModeline(e,t,i,o,n){this.modelineSurface&&this.modelineSurface.drawText(e,t,i,o,n)}touch(){this.mainSurface.touch(),this.modelineSurface&&this.modelineSurface.touch()}makeSurface(e,t){switch(e){case"html":return this.makeHTMLSurface(t);case"editor":return this.makeEditorSurface();default:console.error(`unknown type: ${e}`)}}makeHTMLSurface(e){return new HTMLSurface({editor:this.editor,x:this.x,y:this.y,width:this.width,height:this.height,styles:getViewStyle(this.kind,this.option),isFloating:this.kind==="floating",border:this.border,html:e})}makeEditorSurface(){return new CanvasSurface({option:this.editor.option,x:this.x,y:this.y,width:this.width,height:this.height,styles:getViewStyle(this.kind,this.option),editor:this.editor,border:this.border,isFloating:this.kind==="floating",view:this})}makeModelineSurface(){const e=new CanvasSurface({option:this.editor.option,x:this.x,y:this.y+this.height,width:this.width,height:1,editor:this.editor,view:this});return addMouseEventListeners({dom:e.mainDOM,editor:this.editor,isDraggable:!0,draggableStyle:"row-resize"}),e}changeToHTMLContent(e){this.mainSurface.constructor.name==="HTMLSurface"?this.mainSurface.update(e):(this.mainSurface.delete(),this.mainSurface=this.makeHTMLSurface(e))}changeToEditorContent(){this.mainSurface.delete(),this.mainSurface=this.makeEditorSurface()}evalIn(e){return this.mainSurface.evalIn(e)}}function isPasteKeyEvent(r){return isMacOS()?!1:r.ctrlKey&&r.shiftKey&&r.key==="V"}class Input{constructor(e){const t=e.option;this.editor=e,this.composition=!1,this.ignoreKeydownAfterCompositionend=!1,this.span=document.createElement("span"),this.span.style.color=t.foreground,this.span.style.backgroundColor=t.background,this.span.style.position="absolute",this.span.style.zIndex="",this.span.style.top="0",this.span.style.left="0",this.span.style.font=t.font,this.input=document.createElement("input"),this.input.style.backgroundColor="transparent",this.input.style.color="transparent",this.input.style.width="0",this.input.style.padding="0",this.input.style.margin="0",this.input.style.border="none",this.input.style.position="absolute",this.input.style.zIndex="-10",this.input.style.top="0",this.input.style.left="0",this.input.style.font=t.font,this.input.addEventListener("blur",i=>{this.editor.inputEnabled&&this.input.focus()}),this.input.addEventListener("input",i=>{this.editor.inputEnabled&&this.composition===!1&&(this.input.value="",this.span.innerHTML="",this.input.style.width="0",isMacOS()||this.editor.emitInputString(i.data))}),this.input.addEventListener("keydown",i=>{if(this.editor.inputEnabled){if(isPasteKeyEvent(i)){this.editor.jsonrpc.notify("input",{kind:"clipboard-paste"});return}if(i.isComposing||this.composition||i.key==="Process")return;if(this.ignoreKeydownAfterCompositionend&&isSafari){i.preventDefault(),this.ignoreKeydownAfterCompositionend=!1;return}if(!isMacOS()&&!i.ctrlKey&&!i.altKey&&i.key.length===1)return;if(i.preventDefault(),i.isComposing!==!0&&i.code!=="")return setTimeout(()=>{this.composition||(this.editor.emitInput(i),this.input.value="")},0),!1}}),this.input.addEventListener("compositionstart",i=>{this.editor.inputEnabled&&(this.composition=!0,this.span.innerHTML=this.input.value,this.input.style.width=this.span.offsetWidth+"px")}),this.input.addEventListener("compositionupdate",i=>{this.editor.inputEnabled&&(this.span.innerHTML=i.data,this.input.style.width=this.span.offsetWidth+"px")}),this.input.addEventListener("compositionend",i=>{this.editor.inputEnabled&&(this.composition=!1,this.editor.emitInputString(this.input.value),this.input.value="",this.span.innerHTML=this.input.value,this.input.style.width="0",this.ignoreKeydownAfterCompositionend=!0)}),document.body.appendChild(this.input),document.body.appendChild(this.span),this.input.focus()}finalize(){document.body.removeChild(this.input),document.body.removeChild(this.span)}move(e,t){const[i,o]=this.editor.getDisplayRectangle();this.span.style.top=o+t+"px",this.span.style.left=i+e+"px",this.input.style.top=this.span.offsetTop+"px",this.input.style.left=this.span.offsetLeft+"px"}updateForeground(e){this.span.style.color=e}updateBackground(e){this.span.style.backgroundColor=e}}class MessageTable{constructor(){this.map=new Map}register(e,t){for(const i in t){const o=t[i];this.map.set(i,o),e.on(i,o)}}get(e){return this.map.get(e)}}function getDisplayRectangleDefault(){return[0,0,window.innerWidth,window.innerHeight]}class Editor{constructor({getDisplayRectangle:e=getDisplayRectangleDefault,fontName:t,fontSize:i,onLoaded:o,url:n,onExit:s,onClosed:l,onRestart:c,onUserInput:a,onSwitchFile:u}){this.getDisplayRectangle=e,this.option=new Option({fontName:t,fontSize:i}),this.onExit=s,this.onLoaded=o,this.onRestart=c,this.onUserInput=a,this.onSwitchFile=u,this.inputEnabled=!0,this.input=new Input(this),this.cursors=new Map,this.viewMap=new Map,this.jsonrpc=new JSONRPC(n,{onClosed:()=>{l()}}),this.messageTable=new MessageTable,this.messageTable.register(this.jsonrpc,{startup:this.startup.bind(this),"update-foreground":this.updateForeground.bind(this),"update-background":this.updateBackground.bind(this),"make-view":this.makeView.bind(this),"delete-view":this.deleteView.bind(this),"resize-view":this.resize.bind(this),"move-view":this.move.bind(this),"redraw-view-after":this.redrawViewAfter.bind(this),clear:this.clear.bind(this),"clear-eol":this.clearEol.bind(this),"clear-eob":this.clearEob.bind(this),put:this.put.bind(this),"modeline-put":this.modelinePut.bind(this),"update-display":this.updateDisplay.bind(this),"move-cursor":this.moveCursor.bind(this),"change-view":this.changeView.bind(this),"resize-display":this.resizeDisplay.bind(this),bulk:this.bulk.bind(this),exit:this.exitEditor.bind(this),"user-input":this.userInput.bind(this),"switch-file":this.switchFile.bind(this),"get-clipboard-text":this.getClipboardText.bind(this),"set-clipboard-text":this.setClipboardText.bind(this),"js-eval":this.jsEval.bind(this)}),this.login(),this.boundedHandleResize=this.handleResize.bind(this)}init(){window.addEventListener("resize",this.boundedHandleResize),document.getElementsByTagName("html")[0].style["background-color"]="#333"}finalize(){window.removeEventListener("resize",this.boundedHandleResize),this.input.finalize()}closeConnection(){this.jsonrpc.close()}emitInput(e){const t=convertKeyEvent(e);if(t){if(t.key==="]"&&t.ctrl&&!t.meta&&!t.super&&!t.shift){this.jsonrpc.notify("input",{kind:"abort"});return}this.jsonrpc.notify("input",{kind:"key",value:t})}}emitInputString(e){e?this.jsonrpc.notify("input",{kind:"input-string",value:e}):console.error("unexpected argument",e)}handleResize(e){this.jsonrpc.notify("redraw",{size:this.getDisplaySize()})}enableInput(){this.inputEnabled=!0}disableInput(){this.inputEnabled=!1}sendNotification(e,t){this.jsonrpc.notify(e,t)}request(e,t,i){this.jsonrpc.request(e,t,i)}getDisplaySize(){const[e,t,i,o]=this.getDisplayRectangle(),n=Math.round(i/this.option.fontWidth),s=Math.round(o/this.option.fontHeight);return{width:n,height:s}}callMessage(e,t){this.messageTable.get(e)(t)}findViewById(e){return this.viewMap.get(e)}login(){this.jsonrpc.request("login",{size:this.getDisplaySize(),foreground:this.option.foreground,background:this.option.background},e=>{if(this.updateForeground(e.foreground),this.updateBackground(e.background),e.views)for(const t of e.views)this.makeView(t);this.jsonrpc.notify("redraw",{size:this.getDisplaySize()}),this.jsonrpc.request("user-file-map",{},t=>{this.onSwitchFile(t)})})}startup(){this.onRestart&&this.onRestart()}updateForeground(e){this.option.foreground=e,this.input.updateForeground(e)}updateBackground(e){this.option.background=e,this.input.updateBackground(e);const t=getLemEditorElement();t.style.backgroundColor=e}makeView({id:e,x:t,y:i,width:o,height:n,use_modeline:s,kind:l,type:c,content:a,border:u}){const h=new View({option:this.option,id:e,x:t,y:i,width:o,height:n,useModeline:s,kind:l,type:c,content:a,border:u,editor:this});this.viewMap.set(e,h)}deleteView({viewInfo:{id:e}}){this.findViewById(e).delete(),this.viewMap.delete(e)}resize({viewInfo:{id:e},width:t,height:i}){this.findViewById(e).resize(t,i)}move({viewInfo:{id:e},x:t,y:i}){this.findViewById(e).move(t,i)}redrawViewAfter({viewInfo:{id:e},html:t}){this.findViewById(e).touch()}clear({viewInfo:{id:e}}){this.findViewById(e).clear()}clearEol({viewInfo:{id:e},x:t,y:i}){this.findViewById(e).clearEol(t,i)}clearEob({viewInfo:{id:e},x:t,y:i}){this.findViewById(e).clearEob(t,i)}put({viewInfo:{id:e},x:t,y:i,text:o,textWidth:n,attribute:s,cursorInfo:l}){const c=this.findViewById(e);if(c.print(t,i,o,n,s),l){const{name:a,color:u}=l;let h=this.cursors.get(a);h||(h=new Cursor(this,a,u),this.cursors.set(a,h)),h.move((c.x+t)*this.option.fontWidth,(c.y+i-1)*this.option.fontHeight)}}modelinePut({viewInfo:{id:e},x:t,y:i,text:o,textWidth:n,attribute:s}){this.findViewById(e).printToModeline(t,i,o,n,s)}updateDisplay(){}moveCursor({viewInfo:{id:e},x:t,y:i}){const o=this.findViewById(e),n=o.x*this.option.fontWidth+t*this.option.fontWidth,s=o.y*this.option.fontHeight+i*this.option.fontHeight;this.input.move(n,s)}changeView({viewInfo:{id:e},type:t,content:i}){const o=this.findViewById(e);switch(t){case"html":o.changeToHTMLContent(i);break;case"editor":o.changeToEditorContent();break}}resizeDisplay({width:e,height:t}){const i=getLemEditorElement();i.style.width=Math.floor(e*this.option.fontWidth)+"px",i.style.height=Math.floor(t*this.option.fontHeight)+"px"}bulk(e){this.onLoaded&&(this.onLoaded(),this.onLoaded=null);for(const{method:t,argument:i}of e)this.callMessage(t,i)}exitEditor(){this.onExit&&this.onExit()}userInput({value:e}){this.onUserInput&&this.onUserInput(e)}switchFile(e){this.onSwitchFile&&this.onSwitchFile(e)}getClipboardText(){navigator.clipboard.readText().then(e=>{this.jsonrpc.notify("got-clipboard-text",{text:e})})}setClipboardText({text:e}){navigator.clipboard&&navigator.clipboard.writeText(e)}jsEval({viewInfo:{id:e},code:t}){const o=this.findViewById(e).evalIn(t);return o&&o.toString()}}const canvas=document.querySelector("#editor");function main(){document.fonts.ready.then(()=>{new Editor({canvas,fontName:"Monospace",fontSize:19,onLoaded:null,url:`ws://${window.location.hostname}:50000`,onExit:null,onClosed:null,onRestart:null,onUserInput:null}).init()})}main(); diff --git a/frontends/server/frontend/dist/index.html b/frontends/server/frontend/dist/index.html new file mode 100644 index 000000000..6947499e0 --- /dev/null +++ b/frontends/server/frontend/dist/index.html @@ -0,0 +1,23 @@ + + + + + + Lem + + + + + + +
+ + diff --git a/frontends/server/frontend/editor.js b/frontends/server/frontend/editor.js new file mode 100644 index 000000000..692c36512 --- /dev/null +++ b/frontends/server/frontend/editor.js @@ -0,0 +1,1242 @@ +"use strict"; + +import { JSONRPC } from './jsonrpc.js'; +import * as keyevent from './keyevent.js'; +import * as meaw from 'meaw'; + +const textOffsetY = 5; + +const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); + +function isWideChar(c) { + switch (meaw.getEAW(c)) { + case 'A': + case 'F': + case 'W': + return true; + default: + return false; + } +} + +function isMacOS() { + return window.navigator.userAgent.indexOf('Mac OS X') !== -1; +} + +function computeFontSize(font) { + + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + ctx.font = font; + + const textMetrics = ctx.measureText('W'); + + return [ + Math.floor(textMetrics.width), + Math.round(textMetrics.fontBoundingBoxAscent + textOffsetY + (textMetrics.emHeightDescent || 0)), + ]; +} + +function drawBlock({ ctx, x, y, width, height, style }) { + ctx.fillStyle = style; + ctx.fillRect(x, y, width, height); +} + +function drawText({ ctx, x, y, text, font, style, option }) { + y += Math.round(textOffsetY); // 少しずらしておかないと上の部分が現在行からはみ出して、その行だけ再描画しても描画跡が残ってしまう + ctx.fillStyle = style; + ctx.font = font; + ctx.textBaseline = 'top'; + + for (const c of text) { + if (isWideChar(c)) { + ctx.fillText(c, x, y, option.fontWidth * 2); + x += option.fontWidth * 2; + } else { + ctx.fillText(c, x, y, option.fontWidth); + x += option.fontWidth; + } + } +} + +function drawHorizontalLine({ ctx, x, y, width, style, lineWidth = 1 }) { + ctx.strokeStyle = style; + ctx.lineWidth = lineWidth; + ctx.setLineDash = []; + ctx.beginPath(); + ctx.moveTo(x, y); + ctx.lineTo(x + width, y); + ctx.stroke(); +} + +class Option { + constructor({ fontName, fontSize }) { + const font = fontSize + 'px ' + fontName; + this.font = font; + const [width, height] = computeFontSize(font); + this.fontWidth = width; + this.fontHeight = height; + this.foreground = '#333'; + this.background = '#ccc'; + } +} + +function getLemEditorElement() { + return document.getElementById('lem-editor'); +} + +class Cursor { + constructor(editor, name, color) { + this.editor = editor; + this.name = name; + this.color = color; + + this.span = document.createElement('span'); + this.span.style.all = 'none'; + this.span.style.position = 'absolute'; + this.span.style.zIndex = ''; + this.span.style.top = '0'; + this.span.style.left = '0'; + this.span.style.fontFamily = editor.option.font; + this.span.style.backgroundColor = color; + this.span.style.color = 'white'; + this.span.innerHTML = ''; + document.body.appendChild(this.span); + + this.timerId = null; + } + + move(x, y) { + const [x0, y0] = this.editor.getDisplayRectangle(); + this.span.style.visibility = 'visible'; + this.span.textContent = this.name; + this.span.style.left = (x + x0) + 'px'; + this.span.style.top = (y + y0) + 'px'; + this.span.style.padding = '3px 1%' + if (this.timerId) { + clearTimeout(this.timerId); + } + this.timerId = setTimeout( + () => { + this.span.style.visibility = 'hidden'; + }, + 500, + ); + } +} + +function addMouseEventListeners({dom, editor, isDraggable, draggableStyle}) { + dom.addEventListener('contextmenu', (event) => { + event.preventDefault(); + }); + + const handleMouseDownUp = (event, eventName) => { + event.preventDefault(); + const [displayX, displayY] = editor.getDisplayRectangle(); + + const pixelX = (event.clientX - displayX); + const pixelY = (event.clientY - displayY); + const x = Math.floor(pixelX / editor.option.fontWidth); + const y = Math.floor(pixelY / editor.option.fontHeight); + + editor.jsonrpc.notify('input', { + kind: eventName, + value: { + x: x, + y: y, + pixelX: pixelX, + pixelY: pixelY, + button: event.button, + clicks: event.detail, + } + }); + }; + + dom.addEventListener('mousedown', (event) => { + if (isDraggable) document.body.style.cursor = draggableStyle; + handleMouseDownUp(event, 'mousedown'); + }); + + dom.addEventListener('mouseup', (event) => { + if (isDraggable) document.body.style.cursor = 'default'; + handleMouseDownUp(event, 'mouseup'); + }); + + let lastMouseMoveTime = 0; + dom.addEventListener('mousemove', (event) => { + event.preventDefault(); + const now = Date.now(); + if (now - lastMouseMoveTime > 50) { + lastMouseMoveTime = now; + const [displayX, displayY] = editor.getDisplayRectangle(); + + const pixelX = (event.clientX - displayX); + const pixelY = (event.clientY - displayY); + const x = Math.floor(pixelX / editor.option.fontWidth); + const y = Math.floor(pixelY / editor.option.fontHeight); + editor.jsonrpc.notify('input', { + kind: 'mousemove', + value: { + x: x, + y: y, + pixelX: pixelX, + pixelY: pixelY, + button: event.buttons === 0 ? null : event.buttons - 1, + } + }); + } + }); + + if (isDraggable) { + dom.addEventListener('mouseover', () => { + document.body.style.cursor = draggableStyle; + }); + dom.addEventListener('mouseout', (e) => { + if (e.buttons !== 1) { + document.body.style.cursor = 'default'; + } + }); + } + + dom.addEventListener('wheel', (event) => { + event.preventDefault(); + const [displayX, displayY] = editor.getDisplayRectangle(); + const pixelX = (event.clientX - displayX); + const pixelY = (event.clientY - displayY); + const x = Math.floor(pixelX / editor.option.fontWidth); + const y = Math.floor(pixelY / editor.option.fontHeight); + + editor.jsonrpc.notify('input', { + kind: 'wheel', + value: { + pixelX: pixelX, + pixelY: pixelY, + x: x, + y: y, + wheelX: -Math.round(event.deltaX * 0.01), + wheelY: -Math.round(event.deltaY * 0.01), + }, + }); + }); +} + +const borderOffsetX = 5; +const borderOffsetY = 10; + +class BaseSurface { + constructor({ editor }) { + this.editor = editor; + this.mainDOM = null; + this.wrapper = null; + } + + delete() { + if (this.wrapper) { + getLemEditorElement().removeChild(this.wrapper); + } else { + getLemEditorElement().removeChild(this.mainDOM); + } + } + + setupDOM({ dom, isFloating, border }) { + this.mainDOM = dom; + + if (isFloating && border) { + this.wrapper = document.createElement('div'); + this.wrapper.style.position = 'absolute'; + this.wrapper.style.padding = '10px'; + this.wrapper.style.border = '1px solid'; + this.wrapper.style.borderColor = this.editor.option.foreground; + this.wrapper.style.backgroundColor = this.editor.option.background; + this.wrapper.appendChild(dom); + getLemEditorElement().appendChild(this.wrapper); + } else { + getLemEditorElement().appendChild(dom); + } + } + + move(x, y) { + const [x0, y0] = this.editor.getDisplayRectangle(); + const left = Math.floor(x0 + x * this.editor.option.fontWidth); + const top = Math.floor(y0 + y * this.editor.option.fontHeight); + if (this.wrapper) { + this.wrapper.style.left = left - borderOffsetX + 'px'; + this.wrapper.style.top = top - borderOffsetY + 'px'; + this.mainDOM.style.left = borderOffsetX + 'px'; + this.mainDOM.style.top = borderOffsetY + 'px'; + } else { + this.mainDOM.style.left = left + 'px'; + this.mainDOM.style.top = top + 'px'; + } + } + + resize(width, height) { + const ratio = window.devicePixelRatio || 1; + this.mainDOM.width = width * this.editor.option.fontWidth * ratio; + this.mainDOM.height = height * this.editor.option.fontHeight * ratio; + this.mainDOM.style.width = width * this.editor.option.fontWidth + 'px'; + this.mainDOM.style.height = height * this.editor.option.fontHeight + 'px'; + + const ctx = this.mainDOM.getContext('2d'); + ctx.scale(ratio, ratio); + + if (this.wrapper) { + this.wrapper.style.width = width * this.editor.option.fontWidth + borderOffsetX * 2 + 'px'; + this.wrapper.style.height = height * this.editor.option.fontHeight + borderOffsetY * 2 + 'px'; + } + } + + drawBlock(x, y, width, height, color) { } + drawText(x, y, text, textWidth, attribute) { } + + touch() { + return; + } + + evalIn(code) { + return eval(code); + } +} + +class CanvasSurface extends BaseSurface { + constructor({ editor, view, x, y, width, height, styles, isFloating, border }) { + super({ editor }); + + const canvas = this.setupCanvas(styles); + this.setupDOM({ dom: canvas, isFloating, border }); + this.move(x, y); + this.resize(width, height); + + this.drawingQueue = []; + + addMouseEventListeners({dom:canvas, editor}); + } + + setupCanvas(styles) { + const canvas = document.createElement('canvas'); + canvas.style.position = 'absolute'; + if (styles) { + for (let key in styles) { + canvas.style[key] = styles[key]; + } + } + return canvas; + } + + drawBlock(x, y, width, height, color) { + const option = this.editor.option; + this.drawingQueue.push(function(ctx) { + drawBlock({ + ctx, + x: x * option.fontWidth, + y: y * option.fontHeight, + width: width * option.fontWidth, + height: height * option.fontHeight, + style: color, + }) + }); + } + + drawText(x, y, text, textWidth, attribute) { + const option = this.editor.option; + this.drawingQueue.push(function(ctx) { + if (!attribute) { + drawBlock({ + ctx, + x: x * option.fontWidth, + y: y * option.fontHeight, + width: textWidth * option.fontWidth, + height: option.fontHeight, + style: option.background, + }); + drawText({ + ctx, + x: x * option.fontWidth, + y: y * option.fontHeight, + text: text, + style: option.foreground, + font: option.font, + option, + }); + } else { + let { foreground, background, bold, reverse, underline } = attribute; + if (!foreground) { + foreground = option.foreground; + } + if (!background) { + background = option.background; + } + if (reverse) { + const tmp = background; + background = foreground; + foreground = tmp; + } + const gx = x * option.fontWidth; + const gy = y * option.fontHeight; + drawBlock({ + ctx, + x: gx, + y: gy, + width: textWidth * option.fontWidth, + height: option.fontHeight, + style: background, + }); + drawText({ + ctx, + x: gx, + y: gy, + text: text, + style: foreground, + font: bold ? ('bold ' + option.font) : option.font, + option, + }); + if (underline) { + drawHorizontalLine({ + ctx, + x: gx, + y: gy + option.fontHeight - 2, + width: textWidth * option.fontWidth, + style: typeof (underline) === 'string' ? underline : foreground, + lineWidth: 2 + }); + } + } + }); + } + + touch() { + const ctx = this.mainDOM.getContext('2d'); + for (let fn of this.drawingQueue) { + fn(ctx); + } + this.drawingQueue = []; + } +} + +class HTMLSurface extends BaseSurface { + constructor({ editor, x, y, width, height, styles, isFloating, border, html }) { + super({ editor }); + + const iframe = document.createElement('iframe'); + this.setupDOM({ dom: iframe, isFloating, border }); + iframe.style.position = 'absolute'; + iframe.style.backgroundColor = 'white'; + iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin'); + iframe.srcdoc = html; + + this.iframe = iframe; + + this.move(x, y); + this.resize(width, height); + } + + update(content) { + const scrollY = this.iframe.contentWindow.scrollY; + this.iframe.srcdoc = content; + this.iframe.onload = () => { + this.iframe.onload = null; + this.iframe.contentWindow.scrollTo(0, scrollY); + }; + } + + evalIn(code) { + return this.iframe.contentWindow.eval(code); + } +} + +class VerticalBorder { + constructor({ x, y, height, option, editor }) { + this.option = option; + this.editor = editor; + this.line = document.createElement('div'); + this.line.style.backgroundColor = option.foreground; + this.line.style.width = '5px'; + this.line.style.height = height * option.fontHeight + 'px'; + this.line.style.position = 'absolute'; + + getLemEditorElement().appendChild(this.line); + + this.move(x, y); + + addMouseEventListeners({dom:this.line, editor, isDraggable: true, draggableStyle: 'col-resize'}); + } + + delete() { + this.line.parentNode.removeChild(this.line); + } + + move(x, y) { + const [x0, y0] = this.editor.getDisplayRectangle(); + this.line.style.left = Math.floor(x0 + x * this.option.fontWidth - this.option.fontWidth / 2) + 'px'; + this.line.style.top = (y0 + y * this.option.fontHeight) + 'px'; + } + + resize(height) { + this.line.style.height = height * this.option.fontHeight + 'px'; + } +} + +const viewStyles = { + tile: () => { }, + floating: (option) => ({ + boxSizing: 'border-box', + borderColor: option.foreground, + backgroundColor: option.background, + }), +}; + +function getViewStyle(kind, option) { + return viewStyles[kind](option) || {}; +} + +class View { + constructor({ + id, + x, + y, + width, + height, + useModeline, + kind, + type, + content, + border, + option, + editor, + }) { + this.option = option; + this.id = id; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.useModeline = useModeline; + this.kind = kind; + this.type = type; + this.border = border; + this.editor = editor; + + this.leftsideBar = null; + + switch (kind) { + case 'tile': + this.mainSurface = this.makeSurface(type, content); + this.leftSideBar = new VerticalBorder({ + x: x, + y: y, + height: height + (useModeline ? 1 : 0), + option: option, + editor: editor, + }); + break; + case 'floating': + this.mainSurface = this.makeSurface(type, content); + break; + } + + this.modelineSurface = useModeline ? this.makeModelineSurface() : null; + } + + delete() { + this.mainSurface.delete(); + if (this.modelineSurface) { + this.modelineSurface.delete(); + } + if (this.leftSideBar) { + this.leftSideBar.delete(); + } + } + + move(x, y) { + this.x = x; + this.y = y; + + this.mainSurface.move(x, y); + if (this.modelineSurface) { + this.modelineSurface.move(x, y + this.height); + } + if (this.leftSideBar) { + this.leftSideBar.move(x, y); + } + } + + resize(width, height) { + this.width = width; + this.height = height; + this.mainSurface.resize(width, height); + if (this.modelineSurface) { + this.modelineSurface.move( + this.x, + this.y + this.height, + ); + this.modelineSurface.resize(width, 1); + } + if (this.leftSideBar) { + this.leftSideBar.resize(height + 1); + } + } + + clear() { + this.mainSurface.drawBlock( + 0, + 0, + this.width, + this.height, + this.option.background, + ); + } + + clearEol(x, y) { + this.mainSurface.drawBlock( + x, + y, + this.width - x, + 1, + this.option.background, + ); + } + + clearEob(x, y) { + this.mainSurface.drawBlock( + x, // x === 0 + y, + this.width, + this.height - y, + this.option.background, + ); + } + + print(x, y, text, textWidth, attribute) { + this.mainSurface.drawText( + x, + y, + text, + textWidth, + attribute, + ); + } + + printToModeline(x, y, text, textWidth, attribute) { + if (this.modelineSurface) { + this.modelineSurface.drawText( + x, + y, + text, + textWidth, + attribute, + ); + } + } + + touch() { + this.mainSurface.touch(); + if (this.modelineSurface) { + this.modelineSurface.touch(); + } + } + + makeSurface(type, content) { + switch (type) { + case 'html': + return this.makeHTMLSurface(content); + case 'editor': + return this.makeEditorSurface(); + default: + console.error(`unknown type: ${type}`); + } + } + + makeHTMLSurface(content) { + return new HTMLSurface({ + editor: this.editor, + x: this.x, + y: this.y, + width: this.width, + height: this.height, + styles: getViewStyle(this.kind, this.option), + isFloating: this.kind === 'floating', + border: this.border, + html: content, + }); + } + + makeEditorSurface() { + return new CanvasSurface({ + option: this.editor.option, + x: this.x, + y: this.y, + width: this.width, + height: this.height, + styles: getViewStyle(this.kind, this.option), + editor: this.editor, + border: this.border, + isFloating: this.kind === 'floating', + view: this, + }) + } + + makeModelineSurface() { + const surface = new CanvasSurface({ + option: this.editor.option, + x: this.x, + y: this.y + this.height, + width: this.width, + height: 1, + editor: this.editor, + view: this, + }); + addMouseEventListeners({ + dom: surface.mainDOM, + editor: this.editor, + isDraggable: true, + draggableStyle: 'row-resize', + }); + return surface; + } + + changeToHTMLContent(content) { + if (this.mainSurface.constructor.name === 'HTMLSurface') { + this.mainSurface.update(content); + } else { + this.mainSurface.delete(); + this.mainSurface = this.makeHTMLSurface(content); + } + } + + changeToEditorContent() { + this.mainSurface.delete(); + this.mainSurface = this.makeEditorSurface(); + } + + evalIn(code) { + return this.mainSurface.evalIn(code); + } +} + +function isPasteKeyEvent(event) { + if (isMacOS()) { + // TODO + return false; + } else { + return (event.ctrlKey && event.shiftKey && event.key === 'V'); + } +} + +class Input { + constructor(editor) { + const option = editor.option; + this.editor = editor; + + this.composition = false; + this.ignoreKeydownAfterCompositionend = false; + + this.span = document.createElement('span'); + this.span.style.color = option.foreground; + this.span.style.backgroundColor = option.background; + this.span.style.position = 'absolute'; + this.span.style.zIndex = ''; + this.span.style.top = '0'; + this.span.style.left = '0'; + this.span.style.font = option.font; + + this.input = document.createElement('input'); + this.input.style.backgroundColor = 'transparent'; + this.input.style.color = 'transparent'; + this.input.style.width = '0'; + this.input.style.padding = '0'; + this.input.style.margin = '0'; + this.input.style.border = 'none'; + this.input.style.position = 'absolute'; + this.input.style.zIndex = '-10'; + this.input.style.top = '0'; + this.input.style.left = '0'; + this.input.style.font = option.font; + + this.input.addEventListener('blur', (event) => { + if (this.editor.inputEnabled) { + this.input.focus(); + } + }); + + this.input.addEventListener('input', (event) => { + //console.log('input', event); + if (this.editor.inputEnabled) { + if (this.composition === false) { + this.input.value = ''; + this.span.innerHTML = ''; + this.input.style.width = '0'; + if (!isMacOS()) { + this.editor.emitInputString(event.data); + } + //console.log('>>> input', event.data); + } + } + }); + + this.input.addEventListener('keydown', (event) => { + //console.log('keydown', event, event.isComposing, this.composition); + if (this.editor.inputEnabled) { + + if (isPasteKeyEvent(event)) { + this.editor.jsonrpc.notify('input', { kind: 'clipboard-paste' }); + return; + } + + if (event.isComposing || this.composition) { + return; + } + + // IMEに変換を指示する値はlemに渡すと誤作動するのでreturnする + if (event.key === 'Process') { + return; + } + + if (this.ignoreKeydownAfterCompositionend && isSafari) { + // safariではIMの入力中にバックスペースやエンターキーの入力によってcompositionendイベントが来ると + // その後にkeydownイベントが即座に来る + // それを処理してしまうと余分に改行されたり文字が消えるので無視する + event.preventDefault(); + this.ignoreKeydownAfterCompositionend = false; + return; + } + + if (!isMacOS()) { + // 修飾キーなしで、ReturnやBackspaceではなく'a'などの入力であるか(event.key.length === 1) + if (!event.ctrlKey && !event.altKey && event.key.length === 1) { + // そうであれば'input' eventが受け取れるようにここでreturnする + return; + } + } + + event.preventDefault(); + + if (event.isComposing !== true && event.code !== '') { + // mac/chromium系のブラウザで"あ"と入力すると、 + // keydownの後にcompositionstartが来るので、 + // それを逆転するためにsetTimeoutを使う + setTimeout(() => { + if (!this.composition) { + this.editor.emitInput(event); + this.input.value = ''; + } + }, 0); + return false; + } + } + }); + + this.input.addEventListener('compositionstart', (event) => { + //console.log('compositionstart', event); + if (this.editor.inputEnabled) { + this.composition = true; + this.span.innerHTML = this.input.value; + this.input.style.width = this.span.offsetWidth + 'px'; + } + }); + + this.input.addEventListener('compositionupdate', (event) => { + //console.log('compositionupdate', event); + if (this.editor.inputEnabled) { + this.span.innerHTML = event.data; + this.input.style.width = this.span.offsetWidth + 'px'; + } + }); + + this.input.addEventListener('compositionend', (event) => { + //console.log('compositionend', event); + if (this.editor.inputEnabled) { + this.composition = false; + this.editor.emitInputString(this.input.value); + this.input.value = ''; + this.span.innerHTML = this.input.value; + this.input.style.width = '0'; + this.ignoreKeydownAfterCompositionend = true; + } + }); + + document.body.appendChild(this.input); + document.body.appendChild(this.span); + this.input.focus(); + } + + finalize() { + document.body.removeChild(this.input); + document.body.removeChild(this.span); + } + + move(left, top) { + const [x0, y0] = this.editor.getDisplayRectangle(); + this.span.style.top = (y0 + top) + 'px'; + this.span.style.left = (x0 + left) + 'px'; + this.input.style.top = this.span.offsetTop + 'px'; + this.input.style.left = this.span.offsetLeft + 'px'; + } + + updateForeground(color) { + this.span.style.color = color; + } + + updateBackground(color) { + this.span.style.backgroundColor = color; + } +} + +class MessageTable { + constructor() { + this.map = new Map(); + } + + register(jsonrpc, table) { + for (const method in table) { + const handler = table[method]; + this.map.set(method, handler); + jsonrpc.on(method, handler); + } + } + + get(method) { + return this.map.get(method); + } +} + +function getDisplayRectangleDefault() { + return [0, 0, window.innerWidth, window.innerHeight]; +} + +export class Editor { + constructor({ + getDisplayRectangle = getDisplayRectangleDefault, + fontName, + fontSize, + onLoaded, + url, + onExit, + onClosed, + onRestart, + onUserInput, + onSwitchFile, + }) { + this.getDisplayRectangle = getDisplayRectangle; + + this.option = new Option({ fontName, fontSize }); + + this.onExit = onExit; + this.onLoaded = onLoaded; + this.onRestart = onRestart; + this.onUserInput = onUserInput; + this.onSwitchFile = onSwitchFile; + this.inputEnabled = true; + + this.input = new Input(this); + this.cursors = new Map(); + + this.viewMap = new Map(); + + this.jsonrpc = new JSONRPC(url, { + onClosed: () => { + onClosed(); + }, + }); + + this.messageTable = new MessageTable(); + this.messageTable.register(this.jsonrpc, { + 'startup': this.startup.bind(this), + 'update-foreground': this.updateForeground.bind(this), + 'update-background': this.updateBackground.bind(this), + 'make-view': this.makeView.bind(this), + 'delete-view': this.deleteView.bind(this), + 'resize-view': this.resize.bind(this), + 'move-view': this.move.bind(this), + 'redraw-view-after': this.redrawViewAfter.bind(this), + 'clear': this.clear.bind(this), + 'clear-eol': this.clearEol.bind(this), + 'clear-eob': this.clearEob.bind(this), + 'put': this.put.bind(this), + 'modeline-put': this.modelinePut.bind(this), + 'update-display': this.updateDisplay.bind(this), + 'move-cursor': this.moveCursor.bind(this), + 'change-view': this.changeView.bind(this), + 'resize-display': this.resizeDisplay.bind(this), + 'bulk': this.bulk.bind(this), + 'exit': this.exitEditor.bind(this), + 'user-input': this.userInput.bind(this), + 'switch-file': this.switchFile.bind(this), + 'get-clipboard-text': this.getClipboardText.bind(this), + 'set-clipboard-text': this.setClipboardText.bind(this), + 'js-eval': this.jsEval.bind(this), + }); + + this.login(); + + this.boundedHandleResize = this.handleResize.bind(this); + } + + init() { + window.addEventListener('resize', this.boundedHandleResize); + document.getElementsByTagName('html')[0].style['background-color'] = '#333'; + } + + finalize() { + window.removeEventListener('resize', this.boundedHandleResize); + this.input.finalize(); + } + + closeConnection() { + this.jsonrpc.close(); + } + + emitInput(event) { + const key = keyevent.convertKeyEvent(event); + if (!key) return; + + if (key.key === ']' && key.ctrl && !key.meta && !key.super && !key.shift) { + this.jsonrpc.notify('input', { kind: 'abort' }); + return; + } + + this.jsonrpc.notify('input', { kind: 'key', value: key }); + } + + emitInputString(string) { + if (string) { + this.jsonrpc.notify('input', { kind: 'input-string', value: string }); + } else { + console.error('unexpected argument', string); + } + } + + handleResize(event) { + const canResize = true; + if (canResize) { + this.jsonrpc.notify('redraw', { size: this.getDisplaySize() }); + } else { + this.jsonrpc.notify('redraw'); + } + } + + enableInput() { + this.inputEnabled = true; + } + + disableInput() { + this.inputEnabled = false; + } + + sendNotification(method, args) { + this.jsonrpc.notify(method, args); + } + + request(method, args, callback) { + this.jsonrpc.request(method, args, callback); + } + + getDisplaySize() { + const [_x, _y, displayWidth, displayHeight] = this.getDisplayRectangle(); + const width = Math.round(displayWidth / this.option.fontWidth); + const height = Math.round(displayHeight / this.option.fontHeight); + return { width, height }; + } + + callMessage(method, argument) { + this.messageTable.get(method)(argument); + } + + findViewById(id) { + return this.viewMap.get(id); + } + + login() { + this.jsonrpc.request('login', { + size: this.getDisplaySize(), + foreground: this.option.foreground, + background: this.option.background, + }, (response) => { + this.updateForeground(response.foreground); + this.updateBackground(response.background); + if (response.views) { + for (const view of response.views) { + this.makeView(view); + } + } + + this.jsonrpc.notify('redraw', { size: this.getDisplaySize() }); + + this.jsonrpc.request('user-file-map', {}, response => { + this.onSwitchFile(response); + }); + }); + } + + startup() { + if (this.onRestart) { + this.onRestart(); + } + } + + updateForeground(color) { + this.option.foreground = color; + this.input.updateForeground(color); + } + + updateBackground(color) { + this.option.background = color; + this.input.updateBackground(color); + const element = getLemEditorElement(); + element.style.backgroundColor = color; + } + + makeView({ id, x, y, width, height, use_modeline, kind, type, content, border }) { + const view = new View({ + option: this.option, + id: id, + x: x, + y: y, + width: width, + height: height, + useModeline: use_modeline, + kind: kind, + type: type, + content: content, + border: border, + editor: this + }); + this.viewMap.set(id, view); + } + + deleteView({ viewInfo: { id } }) { + const view = this.findViewById(id); + view.delete(); + this.viewMap.delete(id); + } + + resize({ viewInfo: { id }, width, height }) { + const view = this.findViewById(id); + view.resize(width, height); + } + + move({ viewInfo: { id }, x, y }) { + const view = this.findViewById(id); + view.move(x, y); + } + + redrawViewAfter({ viewInfo: { id }, html }) { + const view = this.findViewById(id); + view.touch(); + } + + clear({ viewInfo: { id } }) { + const view = this.findViewById(id); + view.clear(); + } + + clearEol({ viewInfo: { id }, x, y }) { + const view = this.findViewById(id); + view.clearEol(x, y); + } + + clearEob({ viewInfo: { id }, x, y }) { + const view = this.findViewById(id); + view.clearEob(x, y); + } + + put({ viewInfo: { id }, x, y, text, textWidth, attribute, cursorInfo }) { + const view = this.findViewById(id); + view.print(x, y, text, textWidth, attribute); + if (cursorInfo) { + const { name, color } = cursorInfo; + let cursor = this.cursors.get(name); + if (!cursor) { + cursor = new Cursor(this, name, color); + this.cursors.set(name, cursor); + } + + cursor.move( + (view.x + x) * this.option.fontWidth, + (view.y + y - 1) * this.option.fontHeight, + ); + } + } + + modelinePut({ viewInfo: { id }, x, y, text, textWidth, attribute }) { + const view = this.findViewById(id); + view.printToModeline(x, y, text, textWidth, attribute); + } + + updateDisplay() { + } + + moveCursor({ viewInfo: { id }, x, y }) { + const view = this.findViewById(id); + const left = view.x * this.option.fontWidth + x * this.option.fontWidth; + const top = view.y * this.option.fontHeight + y * this.option.fontHeight; + this.input.move(left, top); + } + + changeView({ viewInfo: { id }, type, content }) { + const view = this.findViewById(id); + switch (type) { + case 'html': + view.changeToHTMLContent(content); + break; + case 'editor': + view.changeToEditorContent(); + break; + } + } + + resizeDisplay({ width, height }) { + // TODO: offset + const element = getLemEditorElement(); + element.style.width = Math.floor(width * this.option.fontWidth) + 'px'; + element.style.height = Math.floor(height * this.option.fontHeight) + 'px'; + } + + bulk(messages) { + if (this.onLoaded) { + this.onLoaded(); + this.onLoaded = null; + } + for (const { method, argument } of messages) { + this.callMessage(method, argument); + } + } + + exitEditor() { + if (this.onExit) { + this.onExit(); + } + } + + userInput({ value }) { + if (this.onUserInput) { + this.onUserInput(value); + } + } + + switchFile(userFileMap) { + if (this.onSwitchFile) { + this.onSwitchFile(userFileMap); + } + } + + getClipboardText() { + navigator.clipboard.readText().then(text => { + this.jsonrpc.notify('got-clipboard-text', { text }); + }); + } + + setClipboardText({ text }) { + if (navigator.clipboard) { + navigator.clipboard.writeText(text); + } + } + + jsEval({ viewInfo: { id }, code }) { + const view = this.findViewById(id); + const result = view.evalIn(code); + if (result) { + return result.toString(); + } + return result; + } +} diff --git a/frontends/server/frontend/index.html b/frontends/server/frontend/index.html new file mode 100644 index 000000000..0e177994b --- /dev/null +++ b/frontends/server/frontend/index.html @@ -0,0 +1,23 @@ + + + + + + Lem + + + + + + +
+ + diff --git a/frontends/server/frontend/jsonrpc.js b/frontends/server/frontend/jsonrpc.js new file mode 100644 index 000000000..ff3a21205 --- /dev/null +++ b/frontends/server/frontend/jsonrpc.js @@ -0,0 +1,124 @@ +"use strict"; + +import { + JSONRPCServerAndClient, + JSONRPCServer, + JSONRPCClient, +} from "json-rpc-2.0"; + +export class JSONRPC { + constructor(url, { onConnected, onClosed }) { + this.url = url; + this.onConnected = onConnected; + this.onClosed = onClosed; + + this.messageQueue = []; + this.serverAndClient = null; + this.connect(); + this.connectionEstablished = false; + + this.timerId = null; + this.closed = false; + } + + close() { + if (this.timerId) { + clearTimeout(this.timerId); + } + this.webSocket.close(); + this.closed = true; + } + + on(method, handler) { + this.serverAndClient.addMethod(method, handler); + } + + async requestInternal(method, arg, callback) { + const result = await this.serverAndClient.request(method, arg); + if (callback) { + callback(result); + } + } + + requestMessageQueue() { + this.messageQueue.forEach((value) => { + const [method, arg, callback] = value; + this.requestInternal(method, arg, callback); + }); + this.messageQueue = []; + } + + request(method, arg, callback) { + if (this.webSocket.readyState === WebSocket.OPEN) { + this.requestInternal(method, arg, callback); + } else { + this.messageQueue.push([method, arg, callback]); + } + } + + notify(method, arg) { + //console.log('notify', this.webSocket.readyState); + switch (this.webSocket.readyState) { + case WebSocket.OPEN: + this.serverAndClient.notify(method, arg); + break; + case WebSocket.CLOSED: + break; + } + } + + connect(webSocket) { + if (this.closed) return; + + console.log('connect', this.url); + this.webSocket = new WebSocket(this.url); + + if (!this.serverAndClient) { + this.serverAndClient = new JSONRPCServerAndClient( + new JSONRPCServer(), + new JSONRPCClient((request) => { + try { + //console.log(request); + this.webSocket.send(JSON.stringify(request)); + return Promise.resolve(); + } catch (error) { + return Promise.reject(error); + } + }) + ); + } + + this.webSocket.onmessage = (event) => { + this.serverAndClient.receiveAndSend(JSON.parse(event.data.toString())); + }; + + this.webSocket.onopen = () => { + console.log("WebSocket connection established"); + this.connectionEstablished = true; + if (this.onConnected) { + this.onConnected(); + } + this.requestMessageQueue(); + }; + + this.webSocket.onclose = (event) => { + console.error("WebScoket closed", event); + this.serverAndClient.rejectAllPendingRequests( + `Connection is closed (${event.reason}).` + ); + + if (this.connectionEstablished) { + this.onClosed(); + } + + this.timerId = setTimeout(() => { + this.connect(); + }, 3000); + }; + + this.webSocket.onerror = (error) => { + console.error("WebSocket error:", error); + this.webSocket.close(); + }; + } +} diff --git a/frontends/server/frontend/keyevent.js b/frontends/server/frontend/keyevent.js new file mode 100644 index 000000000..d4d4a61b1 --- /dev/null +++ b/frontends/server/frontend/keyevent.js @@ -0,0 +1,134 @@ +"use strict"; + +const modifierKeys = ["Shift", "Control", "Alt", "Meta"]; + +const convertKeyTable = { + Enter: "Return", + ArrowRight: "Right", + ArrowLeft: "Left", + ArrowUp: "Up", + ArrowDown: "Down", + + '¡': '1', + '™': '2', + '£': '3', + '¢': '4', + '∞': '5', + '§': '6', + '¶': '7', + '•': '8', + 'ª': '9', + 'º': '0', + '–': '-', + '≠': '=', + '“': '[', + '‘': ']', + '«': '\\', + '…': ';', + 'æ': '\'', + '≤': ',', + '≥': '.', + '÷': '/', + '⁄': '!', + '€': '@', + '‹': '#', + '›': '$', + 'fi': '%', + 'fl': '^', + '‡': '&', + '°': '*', + '·': '(', + '‚': ')', + '—': '_', + '±': '+', + '”': '{', + '’': '}', + '»': '|', + 'Ú': ':', + 'Æ': '"', + '¯': '<', + '˘': '>', + '¿': '?', + + 'œ': 'q', + '∑': 'w', + '´': 'e', + '®': 'r', + '†': 't', + '¥': 'y', + '¨': 'u', + 'ˆ': 'i', + 'ø': 'o', + 'π': 'p', + 'å': 'a', + 'ß': 's', + '∂': 'd', + 'ƒ': 'f', + '©': 'g', + '˙': 'h', + '∆': 'j', + '˚': 'k', + '¬': 'l', + 'Ω': 'z', + '≈': 'x', + 'ç': 'c', + '√': 'v', + '∫': 'b', + '˜': 'n', + 'µ': 'm', + + 'Œ': 'Q', + '„': 'W', + '´': 'E', + '‰': 'R', + 'ˇ': 'T', + 'Á': 'Y', + '¨': 'U', + 'ˆ': 'I', + 'Ø': 'O', + '∏': 'P', + 'Å': 'A', + 'Í': 'S', + 'Î': 'D', + 'Ï': 'F', + '˝': 'G', + 'Ó': 'H', + 'Ô': 'J', + '': 'K', + 'Ò': 'L', + '¸': 'Z', + '˛': 'X', + 'Ç': 'C', + '◊': 'V', + 'ı': 'B', + '˜': 'N', + 'Â': 'M', +}; + +function getKey(e) { + if (e.altKey) { + return ( + convertKeyTable[e.key] || + (e.code.startsWith('Key') ? e.code[3].toLowerCase() : null) || + e.key + ); + } + + return convertKeyTable[e.key] || e.key; +} + +export function convertKeyEvent (e) { + if (modifierKeys.indexOf(e.key) !== -1) { + return null; + } + + const key = getKey(e); + + return { + key: key, + ctrl: e.ctrlKey, + meta: e.altKey, + super: e.metaKey, + shift: e.shiftKey, + }; +}; diff --git a/frontends/server/frontend/main.js b/frontends/server/frontend/main.js new file mode 100644 index 000000000..0eca49a82 --- /dev/null +++ b/frontends/server/frontend/main.js @@ -0,0 +1,23 @@ +import { Editor } from './editor.js'; + +const canvas = document.querySelector('#editor'); + +function main() { + document.fonts.ready.then(() => { + const editor = new Editor({ + canvas: canvas, + fontName: 'Monospace', + fontSize: 19, + onLoaded: null, + url: `ws://${window.location.hostname}:50000`, + onExit: null, + onClosed: null, + onRestart: null, + onUserInput: null, + }); + + editor.init(); + }); +} + +main(); diff --git a/frontends/server/frontend/package-lock.json b/frontends/server/frontend/package-lock.json new file mode 100644 index 000000000..5956c8d7a --- /dev/null +++ b/frontends/server/frontend/package-lock.json @@ -0,0 +1,814 @@ +{ + "name": "editor", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "editor", + "version": "0.0.0", + "dependencies": { + "json-rpc-2.0": "^1.7.0", + "meaw": "^8.0.1" + }, + "devDependencies": { + "vite": "^5.2.14" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", + "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", + "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", + "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", + "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", + "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", + "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", + "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", + "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", + "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", + "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", + "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", + "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", + "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", + "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", + "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", + "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/json-rpc-2.0": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/json-rpc-2.0/-/json-rpc-2.0-1.7.0.tgz", + "integrity": "sha512-asnLgC1qD5ytP+fvBP8uL0rvj+l8P6iYICbzZ8dVxCpESffVjzA7KkYkbKCIbavs7cllwH1ZUaNtJwphdeRqpg==" + }, + "node_modules/meaw": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/meaw/-/meaw-8.0.1.tgz", + "integrity": "sha512-yBDo3rgkH2IIG09/xgMJJE7J6A5zYzwdcDW7NuGcz3DEnQqJU1BuxGd/x/nQMqKrIZhFU3sJTfblra30EI/rkg==" + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true + }, + "node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", + "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.24.0", + "@rollup/rollup-android-arm64": "4.24.0", + "@rollup/rollup-darwin-arm64": "4.24.0", + "@rollup/rollup-darwin-x64": "4.24.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", + "@rollup/rollup-linux-arm-musleabihf": "4.24.0", + "@rollup/rollup-linux-arm64-gnu": "4.24.0", + "@rollup/rollup-linux-arm64-musl": "4.24.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", + "@rollup/rollup-linux-riscv64-gnu": "4.24.0", + "@rollup/rollup-linux-s390x-gnu": "4.24.0", + "@rollup/rollup-linux-x64-gnu": "4.24.0", + "@rollup/rollup-linux-x64-musl": "4.24.0", + "@rollup/rollup-win32-arm64-msvc": "4.24.0", + "@rollup/rollup-win32-ia32-msvc": "4.24.0", + "@rollup/rollup-win32-x64-msvc": "4.24.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vite": { + "version": "5.2.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.14.tgz", + "integrity": "sha512-TFQLuwWLPms+NBNlh0D9LZQ+HXW471COABxw/9TEUBrjuHMo9BrYBPrN/SYAwIuVL+rLerycxiLT41t4f5MZpA==", + "dev": true, + "dependencies": { + "esbuild": "^0.20.1", + "postcss": "^8.4.38", + "rollup": "^4.13.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + } + } +} diff --git a/frontends/server/frontend/package.json b/frontends/server/frontend/package.json new file mode 100644 index 000000000..f5f373e56 --- /dev/null +++ b/frontends/server/frontend/package.json @@ -0,0 +1,18 @@ +{ + "name": "editor", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "devDependencies": { + "vite": "^5.2.14" + }, + "dependencies": { + "json-rpc-2.0": "^1.7.0", + "meaw": "^8.0.1" + } +} diff --git a/frontends/server/jsonrpc-stdio-patch.lisp b/frontends/server/jsonrpc-stdio-patch.lisp new file mode 100644 index 000000000..a53da1b60 --- /dev/null +++ b/frontends/server/jsonrpc-stdio-patch.lisp @@ -0,0 +1,28 @@ +(in-package :jsonrpc/transport/stdio) + +(defmethod send-message-using-transport ((transport stdio-transport) connection message) + (let ((json (babel:string-to-octets + (with-output-to-string (s) + (yason:encode message s)))) + (stream (connection-socket connection))) + (format stream "Content-Length: ~A~C~C~:*~:*~C~C" + (length json) + #\Return + #\Newline) + (write-sequence json stream) + (force-output stream))) + +(defmethod receive-message-using-transport ((transport stdio-transport) connection) + (let* ((stream (connection-socket connection)) + (headers (read-headers stream)) + (length (ignore-errors (parse-integer (gethash "content-length" headers))))) + (when length + (let ((body + (with-output-to-string (out) + (loop + :for c := (read-char stream) + :do (write-char c out) + (decf length (babel:string-size-in-octets (string c))) + (when (<= length 0) + (return)))))) + (parse-message body))))) diff --git a/frontends/server/lem-server.asd b/frontends/server/lem-server.asd new file mode 100644 index 000000000..b01dcd450 --- /dev/null +++ b/frontends/server/lem-server.asd @@ -0,0 +1,15 @@ +(defsystem "lem-server" + :depends-on ("lem" + "lem/extensions" + "jsonrpc" + "jsonrpc/transport/stdio" + "jsonrpc/transport/websocket" + "jsonrpc/transport/local-domain-socket" + "command-line-arguments") + :serial t + :components ((:file "jsonrpc-stdio-patch") + (:file "config") + (:file "utils") + (:file "view") + (:file "mouse") + (:file "main"))) diff --git a/frontends/server/main.lisp b/frontends/server/main.lisp new file mode 100644 index 000000000..719b742f3 --- /dev/null +++ b/frontends/server/main.lisp @@ -0,0 +1,719 @@ +(defpackage :lem-server + (:use :cl + :lem-server/utils + :lem-server/view) + (:local-nicknames (:display :lem-core/display) + (:queue :lem/common/queue) + (:mouse :lem-server/mouse)) + (:export :run-tcp-server + :run-stdio-server + :run-websocket-server + :main)) +(in-package :lem-server) + +(defvar *server-runner*) + +(defclass server-runner () + ()) + +;;; +(defclass websocket-server-runner (server-runner) + ((port :initarg :port + :reader websocket-server-runner-port) + (host :initarg :host + :reader websocket-server-runner-host))) + +(defmethod server-listen ((runner websocket-server-runner) server) + (jsonrpc:server-listen server + :mode :websocket + :port (websocket-server-runner-port runner) + :host (websocket-server-runner-host runner) + :clack-handler 'clack-handler)) + +(defun clack-handler (env) + (unless (wsd:websocket-p env) + (let ((path (getf env :path-info))) + (cond ((string= "/" path) + `(200 (:content-type "text/html") + ,(asdf:system-relative-pathname :lem-server + #p"frontend/dist/index.html"))) + ((alexandria:starts-with-subseq "/assets/" path) + `(200 (:content-type "application/javascript") + ,(asdf:system-relative-pathname :lem-server + (format nil + "frontend/dist/~A" + (string-left-trim "/" path))))) + (t + '(200 () ("ok"))))))) + +;;; +(defclass stdio-server-runner (server-runner) + ()) + +(defmethod server-listen ((runner stdio-server-runner) server) + (jsonrpc:server-listen server + :mode :stdio)) + +;;; +(defclass local-domain-socket-server-runner (server-runner) + ((address :initarg :address :reader local-domain-socket-server-runner-address))) + +(defmethod server-listen ((runner local-domain-socket-server-runner) server) + (jsonrpc:server-listen server + :mode :local-domain-socket + :address (local-domain-socket-server-runner-address runner))) + +(defclass server (jsonrpc:server) ()) + +(defclass jsonrpc (lem:implementation) + ((server :initform (make-instance 'server) + :reader jsonrpc-server) + (display-width :initform 80 + :accessor jsonrpc-display-width) + (display-height :initform 24 + :accessor jsonrpc-display-height) + (background-color :accessor jsonrpc-background-color) + (foreground-color :accessor jsonrpc-foreground-color) + (message-queue :initform (queue:make-queue) + :reader jsonrpc-message-queue) + (editor-thread :initform nil + :accessor jsonrpc-editor-thread)) + (:default-initargs + :name :jsonrpc + :redraw-after-modifying-floating-window t + :window-left-margin 1)) + +(defun get-all-views () + (if (null (lem:current-frame)) + (vector) + (coerce + (loop :for frame :in (lem:all-frames) + :append (loop :for window :in (append (lem:frame-header-windows frame) + (lem:window-list frame) + (lem:frame-floating-windows frame)) + :collect (lem:window-view window))) + 'vector))) + +(defmethod resize-display ((jsonrpc jsonrpc) width height) + (setf (jsonrpc-display-width jsonrpc) width + (jsonrpc-display-height jsonrpc) height)) + +(defmethod notify ((jsonrpc jsonrpc) method argument) + (jsonrpc:broadcast (jsonrpc-server jsonrpc) method argument)) + +(defmethod notify* ((jsonrpc jsonrpc) method argument) + (queue:enqueue (jsonrpc-message-queue jsonrpc) + (hash "method" method "argument" argument))) + +(defmethod notify-all ((jsonrpc jsonrpc)) + (let ((argument (coerce (loop :until (queue:empty-p (jsonrpc-message-queue jsonrpc)) + :collect (queue:dequeue (jsonrpc-message-queue jsonrpc))) + 'vector))) + (notify jsonrpc "bulk" argument))) + +(defun handle-login (jsonrpc logged-in-callback params) + (log:info "ready: ~A ~A" jsonrpc/connection:*connection* (pretty-json params)) + (with-error-handler () + (let* ((size (gethash "size" params)) + (foreground (gethash "foreground" params)) + (background (gethash "background" params))) + + (when size + (let ((width (gethash "width" size)) + (height (gethash "height" size))) + (resize-display jsonrpc width height))) + (when background + (alexandria:when-let (color (lem:parse-color background)) + (setf (jsonrpc-background-color jsonrpc) color))) + (when foreground + (alexandria:when-let (color (lem:parse-color foreground)) + (setf (jsonrpc-foreground-color jsonrpc) color))) + (funcall logged-in-callback) + + (let ((response (hash "views" (with-error-handler () (get-all-views)) + "foreground" (lem-core::foreground-color) + "background" (lem-core::background-color) + "size" (hash "width" (lem:display-width) + "height" (lem:display-height))))) + (log:info "login response: ~A" (pretty-json response)) + response)))) + +(defun login (jsonrpc logged-in-callback) + (lambda (params) + (handle-login jsonrpc logged-in-callback params))) + +(defun redraw (args) + (log:info "redraw: ~A" (pretty-json args)) + (with-error-handler () + (let ((size (and args (gethash "size" args)))) + (when size + (let ((width (gethash "width" size)) + (height (gethash "height" size))) + (resize-display (lem:implementation) width height) + (notify (lem:implementation) "resize-display" size))) + (lem:send-event (lambda () + (lem-core::adjust-all-window-size) + (lem:redraw-display :force t)))))) + +(defmethod lem-if:invoke ((jsonrpc jsonrpc) function) + (let ((ready nil)) + (setf (jsonrpc-editor-thread jsonrpc) + (funcall function + (lambda () + (loop :until ready) + (notify jsonrpc "startup" nil)))) + (jsonrpc:expose (jsonrpc-server jsonrpc) + "login" + (login jsonrpc + (lambda () + (setf ready t)))) + (jsonrpc:expose (jsonrpc-server jsonrpc) + "input" + (lambda (args) + (input-callback jsonrpc args))) + (jsonrpc:expose (jsonrpc-server jsonrpc) + "redraw" + 'redraw) + (jsonrpc:expose (jsonrpc-server jsonrpc) + "got-clipboard-text" + 'got-clipboard-text) + + (lem:add-hook lem:*exit-editor-hook* + (lambda () + (notify jsonrpc "exit" nil) + (uiop:quit 0))) + + (server-listen *server-runner* (jsonrpc-server jsonrpc)))) + +(defmethod lem-if:get-background-color ((jsonrpc jsonrpc)) + (jsonrpc-background-color jsonrpc)) + +(defmethod lem-if:get-foreground-color ((jsonrpc jsonrpc)) + (jsonrpc-foreground-color jsonrpc)) + +(defmethod lem-if:update-foreground ((jsonrpc jsonrpc) color-name) + (with-error-handler () + (notify jsonrpc "update-foreground" color-name))) + +(defmethod lem-if:update-background ((jsonrpc jsonrpc) color-name) + (with-error-handler () + (notify jsonrpc "update-background" color-name))) + +(defmethod lem-if:update-cursor-shape ((jsonrpc jsonrpc) cursor-type) + ;; TODO + ) + +(defmethod lem-if:display-width ((jsonrpc jsonrpc)) + (with-error-handler () + (jsonrpc-display-width jsonrpc))) + +(defmethod lem-if:display-height ((jsonrpc jsonrpc)) + (with-error-handler () + (jsonrpc-display-height jsonrpc))) + +(defmethod lem-if:display-title ((jsonrpc jsonrpc)) + ;; TODO + ) + +(defmethod lem-if:set-display-title ((jsonrpc jsonrpc) title) + ;; TODO + ) + +(defmethod lem-if:display-fullscreen-p ((jsonrpc jsonrpc)) + ;; TODO + ) + +(defmethod lem-if:set-display-fullscreen-p ((jsonrpc jsonrpc) fullscreen-p) + ;; TODO + ) + +(defmethod lem-if:make-view ((jsonrpc jsonrpc) window x y width height use-modeline) + (let ((view (make-view :window window + :x x + :y y + :width width + :height height + :use-modeline use-modeline + :kind (if (lem:floating-window-p window) + "floating" + "tile") + :border (and (lem:floating-window-p window) + (lem:floating-window-border window))))) + (notify* jsonrpc "make-view" view) + view)) + +(defmethod lem-if:view-width ((jsonrpc jsonrpc) view) + (view-width view)) + +(defmethod lem-if:view-height ((jsonrpc jsonrpc) view) + (view-height view)) + +(defmethod lem-if:delete-view ((jsonrpc jsonrpc) view) + (with-error-handler () + (notify* jsonrpc "delete-view" (hash "viewInfo" view)))) + +(defmethod lem-if:clear ((jsonrpc jsonrpc) view) + (with-error-handler () + (notify* jsonrpc "clear" (hash "viewInfo" view)))) + +(defmethod lem-if:set-view-size ((jsonrpc jsonrpc) view width height) + (with-error-handler () + (resize-view view width height) + (notify* jsonrpc + "resize-view" + (hash "viewInfo" view + "width" width + "height" height)))) + +(defmethod lem-if:set-view-pos ((jsonrpc jsonrpc) view x y) + (with-error-handler () + (move-view view x y) + (notify* jsonrpc + "move-view" + (hash "viewInfo" view + "x" x + "y" y)))) + +(defmethod lem-if:redraw-view-before ((jsonrpc jsonrpc) view) + ) + +(defmethod lem-if:redraw-view-after ((jsonrpc jsonrpc) view) + (notify* jsonrpc + "redraw-view-after" + (hash "viewInfo" view))) + +(defmethod lem:redraw-buffer ((jsonrpc jsonrpc) (buffer lem:html-buffer) window force) + ) + +(defmethod lem-if:will-update-display ((jsonrpc jsonrpc)) + ) + +(defmethod lem-if:update-display ((jsonrpc jsonrpc)) + (with-error-handler () + (let ((view (lem:window-view (lem:current-window))) + (x (lem:last-print-cursor-x (lem:current-window))) + (y (lem:last-print-cursor-y (lem:current-window)))) + (notify* jsonrpc + "move-cursor" + (hash "viewInfo" view "x" x "y" y))) + (notify* jsonrpc "update-display" nil) + (notify-all jsonrpc))) + +(defvar *clipboard-wait-queue* (lem/common/queue:make-concurrent-queue)) + +(defmethod lem-if:clipboard-paste ((jsonrpc jsonrpc)) + (notify jsonrpc "get-clipboard-text" (hash)) + (lem/common/queue:dequeue *clipboard-wait-queue* :timeout 0.1)) + +(defun got-clipboard-text (params) + (let ((text (gethash "text" params))) + (lem/common/queue:enqueue *clipboard-wait-queue* text))) + +(defmethod lem-if:clipboard-copy ((jsonrpc jsonrpc) text) + (notify jsonrpc "set-clipboard-text" (hash "text" text))) + +(defmethod lem-if:increase-font-size ((jsonrpc jsonrpc)) + ;; TODO + ) + +(defmethod lem-if:decrease-font-size ((jsonrpc jsonrpc)) + ;; TODO + ) + +(defmethod lem-if:resize-display-before ((jsonrpc jsonrpc)) + ) + +(defmethod lem-if:get-font-list ((jsonrpc jsonrpc)) + ) + +(defmethod lem-if:get-mouse-position ((jsonrpc jsonrpc)) + (mouse:get-position)) + +(defmethod lem-if:get-char-width ((jsonrpc jsonrpc)) + ;; TODO + 1) +(defmethod lem-if:get-char-height ((jsonrpc jsonrpc)) + ;; TODO + 1) + +(defmethod lem-if:js-eval ((jsonrpc jsonrpc) view code &key wait) + (let ((params (hash "viewInfo" view "code" code))) + (if wait + (let ((mailbox (sb-concurrency:make-mailbox :name "js-eval-mailbox"))) + (apply #'values + (loop :for connection + :in (jsonrpc/server::server-client-connections + (jsonrpc-server (lem:implementation))) + :do (jsonrpc:call-async-to + (jsonrpc-server (lem:implementation)) + connection + "js-eval" + params + (lambda (res) + (sb-concurrency:send-message mailbox (list t res))) + (lambda (message code) + (sb-concurrency:send-message + mailbox + (list nil + (make-condition 'jsonrpc/errors:jsonrpc-callback-error + :message message + :code code))))))) + (destructuring-bind (ok value) + (sb-concurrency:receive-message mailbox) + (if ok + value + (error value)))) + (notify (lem:implementation) "js-eval" params)))) + +(lem:add-hook lem:*switch-to-buffer-hook* 'on-switch-to-buffer) + +(defun on-switch-to-buffer (buffer) + (cond ((and (typep buffer 'lem:html-buffer) + (lem:html-buffer-updated-p buffer)) + (lem:invalidate-html-buffer-updated buffer) + (notify* (lem:implementation) + "change-view" + (hash "viewInfo" (lem:window-view (lem:current-window)) + "type" "html" + "content" (lem:html-buffer-html buffer)))) + ((and (typep (lem:current-buffer) 'lem:html-buffer) + (not (typep buffer 'lem:html-buffer))) + (notify* (lem:implementation) + "change-view" + (hash "viewInfo" (lem:window-view (lem:current-window)) + "type" "editor"))) + ((and (not (typep (lem:current-buffer) 'lem:html-buffer)) + (typep buffer 'lem:html-buffer)) + (notify* (lem:implementation) + "change-view" + (hash "viewInfo" (lem:window-view (lem:current-window)) + "type" "html" + "content" (lem:html-buffer-html buffer)))))) +;;;; +(defun bool (x) (if x 'yason:true 'yason:false)) + +(defun ensure-rgb (color) + (if (typep color 'lem:color) + (lem:color-to-hex-string color) + color)) + +(defmethod yason:encode ((attribute lem:attribute) &optional (stream *standard-output*)) + (with-error-handler () + (yason:with-output (stream) + (yason:with-object () + (yason:encode-object-element "foreground" (ensure-rgb (lem:attribute-foreground attribute))) + (yason:encode-object-element "background" (ensure-rgb (lem:attribute-background attribute))) + (yason:encode-object-element "reverse" (bool (lem:attribute-reverse attribute))) + (yason:encode-object-element "bold" (bool (lem:attribute-bold attribute))) + (yason:encode-object-element "underline" (lem:attribute-underline attribute)))))) + + + +;;; drawing +(defgeneric object-width (drawing-object)) + +(defmethod object-width ((drawing-object display:void-object)) + 0) + +(defmethod object-width ((drawing-object display:text-object)) + (lem-core:string-width (display:text-object-string drawing-object))) + +(defmethod object-width ((drawing-object display:eol-cursor-object)) + 0) + +(defmethod object-width ((drawing-object display:extend-to-eol-object)) + 0) + +(defmethod object-width ((drawing-object display:line-end-object)) + 0) + +(defmethod object-width ((drawing-object display:image-object)) + 0) + +(defgeneric draw-object (jsonrpc object x y view)) + +(defmethod draw-object (jsonrpc (object display:void-object) x y view) + (values)) + +(defvar *put-target* :edit-area) + +(defun put (jsonrpc view x y string attribute) + (with-error-handler () + (notify* jsonrpc + (ecase *put-target* + (:edit-area "put") + (:modeline "modeline-put")) + (hash "viewInfo" view + "x" x + "y" y + "text" string + "textWidth" (lem:string-width string) + "attribute" (lem:ensure-attribute attribute nil))))) + +(defmethod draw-object (jsonrpc (object display:text-object) x y view) + (let* ((string (display:text-object-string object)) + (attribute (display:text-object-attribute object))) + (when (and attribute (lem-core:cursor-attribute-p attribute)) + (lem-core::set-last-print-cursor (view-window view) x y)) + (put jsonrpc view x y string attribute))) + +(defmethod draw-object (jsonrpc (object display:eol-cursor-object) x y view) + (lem-core::set-last-print-cursor (view-window view) x y) + (put jsonrpc view x y " " + (lem:make-attribute + :background + (lem:color-to-hex-string (display:eol-cursor-object-color object))))) + +(defmethod draw-object (jsonrpc (object display:extend-to-eol-object) x y view) + (let ((width (lem-if:view-width (lem-core:implementation) view))) + (when (< x width) + (put jsonrpc view x y + (make-string (- width x) :initial-element #\space) + (lem:make-attribute + :background + (lem:color-to-hex-string (display:extend-to-eol-object-color object))))))) + +(defmethod draw-object (jsonrpc (object display:line-end-object) x y view) + (let ((string (display:text-object-string object)) + (attribute (display:text-object-attribute object))) + (put jsonrpc + view + (+ x (display:line-end-object-offset object)) + y + string + attribute))) + +(defmethod draw-object (jsonrpc (object display:image-object) x y view) + (values)) + +(defun render-line (jsonrpc view x y objects) + (loop :for object :in objects + :do (draw-object jsonrpc object x y view) + (incf x (object-width object)))) + +(defun render-line-from-behind (jsonrpc view y objects) + (loop :with current-x := (view-width view) + :for object :in objects + :do (decf current-x (object-width object)) + (draw-object jsonrpc object current-x y view))) + +(defmethod lem-if:render-line ((jsonrpc jsonrpc) view x y objects height) + (with-error-handler () + (notify* jsonrpc + "clear-eol" + (hash "viewInfo" view + "x" x + "y" y)) + (render-line jsonrpc view x y objects))) + +(defmethod lem-if:render-line-on-modeline ((jsonrpc jsonrpc) view left-objects right-objects + default-attribute height) + (let ((*put-target* :modeline)) + (with-error-handler () + (notify* jsonrpc + "modeline-put" + (hash "viewInfo" view + "x" 0 + "y" 0 + "text" (make-string (view-width view) :initial-element #\space) + "textWidth" (view-width view) + "attribute" default-attribute)) + (render-line jsonrpc view 0 0 left-objects) + (render-line-from-behind jsonrpc view 0 right-objects)))) + +(defmethod lem-if:object-width ((jsonrpc jsonrpc) drawing-object) + (object-width drawing-object)) + +(defmethod lem-if:object-height ((jsonrpc jsonrpc) drawing-object) + 1) + +(defmethod lem-if:clear-to-end-of-window ((jsonrpc jsonrpc) view y) + (notify* jsonrpc + "clear-eob" + (hash "viewInfo" view + "x" 0 + "y" y))) + + +;;; +(defconstant +abort+ 0) +(defconstant +keyevent+ 1) +(defconstant +resize+ 2) +(defconstant +input-string+ 3) + +(defun convert-keyevent (e) + (let ((key (gethash "key" e)) + (ctrl (gethash "ctrl" e)) + (meta (gethash "meta" e)) + (super (gethash "super" e)) + (shift (gethash "shift" e))) + (cond ((string= key " ") (setf key "Space"))) + (lem:make-key :ctrl ctrl + :meta meta + :super super + :shift (if (lem:insertion-key-sym-p key) + nil + shift) + :sym (if (and (lem:insertion-key-sym-p key) + shift + meta) + (string-upcase key) + key)))) + +(defun convert-button (button) + (case button + (0 :button-1) + (2 :button-3) + (1 :button-2))) + +(defun input-callback (jsonrpc args) + (handler-case + (let ((kind (gethash "kind" args)) + (value (gethash "value" args))) + (alexandria:switch (kind :test #'equal) + ("abort" + (lem:send-abort-event (jsonrpc-editor-thread jsonrpc) nil)) + ("key" + (when value + (notify jsonrpc + "user-input" + (hash "value" value)) + (let ((key (convert-keyevent value))) + (lem:send-event key)))) + ("clipboard-paste" + (lem:send-event + (lambda () + (let ((text (lem:get-clipboard-data)) + (mode (lem:ensure-mode-object + (lem:current-major-mode-at-point + (lem:current-point))))) + (lem:paste-using-mode mode text))))) + ("mousedown" + (let ((x (gethash "x" value)) + (y (gethash "y" value)) + (pixel-x (gethash "pixelX" value)) + (pixel-y (gethash "pixelY" value)) + (button (convert-button (gethash "button" value))) + (clicks (gethash "clicks" value))) + (lem:send-event + (lambda () + (lem:receive-mouse-button-down x + y + pixel-x + pixel-y + button + clicks))))) + ("mouseup" + (let ((x (gethash "x" value)) + (y (gethash "y" value)) + (pixel-x (gethash "pixelX" value)) + (pixel-y (gethash "pixelY" value)) + (button (convert-button (gethash "button" value)))) + (when button + (lem:send-event + (lambda () + (lem:receive-mouse-button-up x + y + pixel-x + pixel-y + button)))))) + ("mousemove" + (let ((x (gethash "x" value)) + (y (gethash "y" value)) + (pixel-x (gethash "pixelX" value)) + (pixel-y (gethash "pixelY" value)) + (button (convert-button (gethash "button" value)))) + (lem:send-event + (lambda () + (mouse:update-position x y) + (lem:receive-mouse-motion x + y + pixel-x + pixel-y + button))))) + ("wheel" + (let ((x (gethash "x" value)) + (y (gethash "y" value)) + (pixel-x (gethash "pixelX" value)) + (pixel-y (gethash "pixelY" value)) + (wheel-x (gethash "wheelX" value)) + (wheel-y (gethash "wheelY" value))) + (lem:send-event + (lambda () + (lem:receive-mouse-wheel x y pixel-x pixel-y wheel-x wheel-y) + (when (= 0 (lem:event-queue-length)) + (lem:redraw-display)))))) + ("resize" + (resize-display jsonrpc + (gethash "width" value) + (gethash "height" value)) + (lem:send-event :resize)) + ("input-string" + (notify jsonrpc + "user-input" + (hash "value" value)) + (loop :for c :across value + :for key := (convert-keyevent + (alexandria:plist-hash-table (list "key" (string c)) + :test 'equal)) + :do (lem:send-event key))) + (otherwise (error "unexpected input kind: ~D" kind)))) + (error (e) + (log:info "input-callback: ~A ~A" e + (with-output-to-string (stream) + (let ((stream (yason:make-json-output-stream stream))) + (yason:encode args stream))))))) + +;;; +(defparameter +command-line-spec+ + '(("mode" :type string :optional t :documentation "\"websocket\", \"stdio\", \"local-domain-socket\"") + ("port" :type integer :optional nil :documentation "port of \"websocket\"") + ("host" :type string :optional t) + ("address" :type string :optional t :documentation "address of \"local-domain-socket\""))) + +(defun run-websocket-server (&key (port 50000) (hostname "127.0.0.1")) + (let ((*server-runner* + (make-instance 'websocket-server-runner + :port port + :host hostname))) + (lem:lem))) + +(defun run-stdio-server () + (let ((*server-runner* (make-instance 'stdio-server-runner))) + (lem:lem))) + +(defun run-local-domain-socket-server (&key address) + (let ((*server-runner* (make-instance 'local-domain-socket-server-runner + :address address))) + (lem:lem))) + +(defun check-port-specified (port) + (unless port + (command-line-arguments:show-option-help +command-line-spec+) + (uiop:quit 1))) + +(defun main (&optional (args (uiop:command-line-arguments))) + (command-line-arguments:handle-command-line + +command-line-spec+ + (lambda (&key (mode "websocket") + port + (host "127.0.0.1") + address) + (vom:config t :info) + (cond ((string= mode "websocket") + (check-port-specified port) + (format t + "~%~4Topen http://localhost:~D/ in your browser~2%" + port) + (run-websocket-server :port port + :hostname host)) + ((string= mode "stdio") + (run-stdio-server)) + ((string= mode "local-domain-socket") + (run-local-domain-socket-server :address address)) + (t + (command-line-arguments:show-option-help +command-line-spec+) + (uiop:quit 1)))) + :name "lem-server" + :positional-arity 0 + :command-line args)) diff --git a/frontends/server/mouse.lisp b/frontends/server/mouse.lisp new file mode 100644 index 000000000..ee96f4a91 --- /dev/null +++ b/frontends/server/mouse.lisp @@ -0,0 +1,15 @@ +(uiop:define-package :lem-server/mouse + (:use :cl) + (:export :update-position + :get-position)) +(in-package :lem-server/mouse) + +(defvar *mouse-x* 0) +(defvar *mouse-y* 0) + +(defun update-position (x y) + (setf *mouse-x* x + *mouse-y* y)) + +(defun get-position () + (values *mouse-x* *mouse-y*)) diff --git a/frontends/server/utils.lisp b/frontends/server/utils.lisp new file mode 100644 index 000000000..f2c01df5b --- /dev/null +++ b/frontends/server/utils.lisp @@ -0,0 +1,31 @@ +(defpackage :lem-server/utils + (:use :cl) + (:export :hash + :with-error-handler + :json-equal + :pretty-json)) +(in-package :lem-server/utils) + +(defun hash (&rest args) + (alexandria:plist-hash-table args :test #'equal)) + +(defmacro with-error-handler (() &body body) + `(handler-case + (handler-bind ((error (lambda (c) + (log:info "~A" + (with-output-to-string (stream) + (format stream "~A~%" c) + (uiop:print-backtrace :stream stream + :condition c) + (force-output stream)))))) + ,@body) + (error ()))) + +(defun json-equal (x y) + (string= (with-output-to-string (out) (yason:encode x out)) + (with-output-to-string (out) (yason:encode y out)))) + +(defun pretty-json (value) + (with-output-to-string (out) + (yason:encode value + (yason:make-json-output-stream out)))) diff --git a/frontends/server/view.lisp b/frontends/server/view.lisp new file mode 100644 index 000000000..e4fba6d14 --- /dev/null +++ b/frontends/server/view.lisp @@ -0,0 +1,62 @@ +(defpackage :lem-server/view + (:use :cl) + (:export :view + :make-view + :view-window + :view-id + :view-x + :view-y + :view-width + :view-height + :view-use-modeline + :view-kind + :move-view + :resize-view)) +(in-package :lem-server/view) + +(defvar *view-id-counter* 0) + +(defstruct (view (:constructor %make-view)) + (id (incf *view-id-counter*)) + window + x + y + width + height + use-modeline + kind ; "tile" / "floating" + border) + +(defun make-view (&rest args &key window x y width height use-modeline kind border) + (declare (ignore window x y width height use-modeline kind border)) + (apply #'%make-view args)) + +(defun move-view (view x y) + (setf (view-x view) x + (view-y view) y)) + +(defun resize-view (view width height) + (setf (view-width view) width + (view-height view) height) + (values)) + +(defmethod yason:encode ((view view) &optional (stream *standard-output*)) + (yason:with-output (stream) + (yason:with-object () + (yason:encode-object-element "id" (view-id view)) + (yason:encode-object-element "x" (view-x view)) + (yason:encode-object-element "y" (view-y view)) + (yason:encode-object-element "width" (view-width view)) + (yason:encode-object-element "height" (view-height view)) + (yason:encode-object-element "use_modeline" (view-use-modeline view)) + (yason:encode-object-element "kind" (view-kind view)) + (yason:encode-object-element "type" + (let ((buffer (lem:window-buffer (view-window view)))) + (if (typep buffer 'lem:html-buffer) + "html" + "editor"))) + (yason:encode-object-element "content" + (let ((buffer (lem:window-buffer (view-window view)))) + (when (typep buffer 'lem:html-buffer) + (lem:html-buffer-html buffer)))) + (yason:encode-object-element "border" (view-border view))))) diff --git a/lem-tests.asd b/lem-tests.asd index 1b8e1c3e7..1ec29f45f 100644 --- a/lem-tests.asd +++ b/lem-tests.asd @@ -8,6 +8,8 @@ "rove") :pathname "tests" :components ((:file "utilities") + (:module "buffer" + :components ((:file "internal"))) (:module "common" :components ((:file "ring") (:file "killring") diff --git a/lem.asd b/lem.asd index c311a5aee..5a01d25ca 100644 --- a/lem.asd +++ b/lem.asd @@ -49,6 +49,7 @@ (:file "queue") (:file "hooks") (:file "var") + (:file "socket") (:file "utils") (:module "character" :serial t @@ -73,6 +74,7 @@ (:file "point") (:file "edit") (:file "mark") + (:file "undo") (:file "buffer-insert") (:file "basic") (:file "syntax-predicates") @@ -132,6 +134,7 @@ (:file "command-advices") (:file "interface") (:file "highlight-line") + (:file "html-buffer") (:file "site-init") (:file "lem") @@ -205,6 +208,10 @@ (:file "detective") (:file "extension-commands"))))) + (:module "ui" + :serial t + :components ((:file "theme-list"))))) + (defsystem "lem/extensions" :depends-on (#+sbcl "lem-welcome" @@ -248,24 +255,19 @@ "lem-base16-themes" #+sbcl "lem-elixir-mode" + "lem-ruby-mode" "lem-erlang-mode" "lem-documentation-mode" "lem-elisp-mode" + "lem-terraform-mode" + "lem-nix-mode" "lem-markdown-mode" "lem-color-preview" - "lem-lua-mode")) - -(defsystem "lem/legit" - :serial t - :depends-on ("lem" "lem-patch-mode" "lem-yaml-mode" "lem-markdown-mode") - :components ((:module "extensions/legit" - :components ((:file "porcelain") - (:file "peek-legit") - (:file "legit") - (:file "legit-rebase") - (:file "legit-commit"))) - (:module "scripts" - :components ((:static-file "dumbrebaseeditor.sh"))))) + "lem-lua-mode" + "lem-terminal" + "lem-legit" + "lem-dashboard" + "lem-copilot")) (defsystem "lem/executable" :build-operation program-op diff --git a/lib/socket-utils/lem-socket-utils.asd b/lib/socket-utils/lem-socket-utils.asd deleted file mode 100644 index 9cce174e9..000000000 --- a/lib/socket-utils/lem-socket-utils.asd +++ /dev/null @@ -1,4 +0,0 @@ -(defsystem "lem-socket-utils" - :depends-on ("usocket") - :serial t - :components ((:file "socket"))) diff --git a/qlfile b/qlfile index 4ce248b06..e85ed2b15 100644 --- a/qlfile +++ b/qlfile @@ -1,6 +1,6 @@ +ql uiop git micros https://github.com/lem-project/micros.git git lem-mailbox https://github.com/lem-project/lem-mailbox.git -git lem-base16-themes https://github.com/lem-project/lem-base16-themes.git git async-process https://github.com/lem-project/async-process.git git sblint https://github.com/cxxxr/sblint.git git rove https://github.com/fukamachi/rove.git diff --git a/qlfile.lock b/qlfile.lock index ecc5faa2d..0c0c4887a 100644 --- a/qlfile.lock +++ b/qlfile.lock @@ -1,23 +1,23 @@ ("quicklisp" . (:class qlot/source/dist:source-dist - :initargs (:distribution "http://beta.quicklisp.org/dist/quicklisp.txt" :%version :latest) - :version "2023-06-18")) + :initargs (:distribution "https://beta.quicklisp.org/dist/quicklisp.txt" :%version :latest) + :version "2024-10-12")) +("uiop" . + (:class qlot/source/ql:source-ql + :initargs (:%version :latest) + :version "ql-2024-10-12")) ("micros" . (:class qlot/source/git:source-git :initargs (:remote-url "https://github.com/lem-project/micros.git") - :version "git-994d4d67467ec1b6eddacad9dba385b42101679e")) + :version "git-af94fe5d6688f67a092f604765fb706ebae44e99")) ("lem-mailbox" . (:class qlot/source/git:source-git :initargs (:remote-url "https://github.com/lem-project/lem-mailbox.git") :version "git-12d629541da440fadf771b0225a051ae65fa342a")) -("lem-base16-themes" . - (:class qlot/source/git:source-git - :initargs (:remote-url "https://github.com/lem-project/lem-base16-themes.git") - :version "git-07dacae6c1807beaeffc730063b54487d5c82eb0")) ("async-process" . (:class qlot/source/git:source-git :initargs (:remote-url "https://github.com/lem-project/async-process.git") - :version "git-9690530fc92b59636d9f17d821afa7697e7c8ca4")) + :version "git-3b16b91d417530dac03559980fb5703206e20c55")) ("sblint" . (:class qlot/source/git:source-git :initargs (:remote-url "https://github.com/cxxxr/sblint.git") @@ -25,7 +25,7 @@ ("rove" . (:class qlot/source/git:source-git :initargs (:remote-url "https://github.com/fukamachi/rove.git") - :version "git-f168cd177b5f83f171dd970dc1a9abb6eb43f044")) + :version "git-cacea7331c10fe9d8398d104b2dfd579bf7ea353")) ("cl-sdl2" . (:class qlot/source/git:source-git :initargs (:remote-url "https://github.com/lem-project/cl-sdl2.git") @@ -33,7 +33,7 @@ ("cl-sdl2-ttf" . (:class qlot/source/git:source-git :initargs (:remote-url "https://github.com/lem-project/cl-sdl2-ttf.git") - :version "git-e61bb2119003d8ae7792d38aa11f7728d3ee5a00")) + :version "git-f43344efe89cf9ce509e6ce4f7303ebb2ff14434")) ("cl-sdl2-image" . (:class qlot/source/git:source-git :initargs (:remote-url "https://github.com/lem-project/cl-sdl2-image.git") @@ -41,4 +41,4 @@ ("jsonrpc" . (:class qlot/source/git:source-git :initargs (:remote-url "https://github.com/cxxxr/jsonrpc.git") - :version "git-bb3a536beb22112e544321dc7f3a8822413f9bac")) \ No newline at end of file + :version "git-bb3a536beb22112e544321dc7f3a8822413f9bac")) diff --git a/scripts/build-server.lisp b/scripts/build-server.lisp new file mode 100644 index 000000000..4d24a8404 --- /dev/null +++ b/scripts/build-server.lisp @@ -0,0 +1,7 @@ +(ql:quickload :lem-server) + +(lem:init-at-build-time) + +(sb-ext:save-lisp-and-die "lem-server" + :toplevel #'lem-server:main + :executable t) diff --git a/scripts/install/lem.desktop b/scripts/install/lem.desktop new file mode 100644 index 000000000..74e93cee1 --- /dev/null +++ b/scripts/install/lem.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Name=Lem +Comment=Common Lisp editor/IDE with high expansibility +Exec=/usr/local/bin/lem -i sdl2 %F +MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;application/x-shellscript;text/x-c;text/x-c++; +Icon=lem +Terminal=false +Type=Application +Categories=Development;TextEditor; \ No newline at end of file diff --git a/scripts/install/lem.ico b/scripts/install/lem.ico new file mode 100644 index 000000000..9867f79b2 Binary files /dev/null and b/scripts/install/lem.ico differ diff --git a/scripts/install/lem.svg b/scripts/install/lem.svg new file mode 100644 index 000000000..d1aa22ae5 --- /dev/null +++ b/scripts/install/lem.svg @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff --git a/src/attribute.lisp b/src/attribute.lisp index cf3067cc9..45e9195a3 100644 --- a/src/attribute.lisp +++ b/src/attribute.lisp @@ -46,11 +46,11 @@ (defun make-attribute (&key foreground background reverse bold underline plist) (make-instance 'attribute - :foreground (or (maybe-base-color foreground) nil) - :background (or (maybe-base-color background) nil) + :foreground (or (ensure-color foreground) nil) + :background (or (ensure-color background) nil) :reverse reverse :bold bold - :underline (or (maybe-base-color underline) underline) + :underline (or (ensure-color underline) underline) :plist plist)) (defun ensure-attribute (x &optional (errorp t)) @@ -177,8 +177,8 @@ ',name)) (define-attribute cursor - (:light :foreground "white" :background "black") - (:dark :foreground "black" :background "white")) + (:light :background "black") + (:dark :background "white")) (define-attribute fake-cursor (:light :foreground "white" :background "blue") @@ -233,7 +233,72 @@ (define-attribute syntax-builtin-attribute (t :foreground "#FF87FF")) - +(define-attribute document-header1-attribute + (:light :foreground "#000000" :bold t) + (:dark :foreground "#FFFFFF" :bold t)) + +(define-attribute document-header2-attribute + (:light :foreground "#005A9C" :bold t) + (:dark :foreground "#87CEFA" :bold t)) + +(define-attribute document-header3-attribute + (:light :foreground "#0000FF" :bold t) + (:dark :foreground "#ADD8E6" :bold t)) + +(define-attribute document-header4-attribute + (:light :foreground "#8B008B" :bold t) + (:dark :foreground "#DDA0DD" :bold t)) + +(define-attribute document-header5-attribute + (:light :foreground "#A0522D" :bold t) + (:dark :foreground "#F4A460" :bold t)) + +(define-attribute document-header6-attribute + (:light :foreground "#2E8B57" :bold t) + (:dark :foreground "#98FB98" :bold t)) + +(define-attribute document-bold-attribute + (t :bold t)) + +(define-attribute document-italic-attribute + (:light :foreground "#8B4513") + (:dark :foreground "#DEB887")) + +(define-attribute document-underline-attribute + (t :underline t)) + +(define-attribute document-link-attribute + (:light :foreground "#0000EE" :underline t) + (:dark :foreground "#87CEFA" :underline t)) + +(define-attribute document-list-attribute + (:light :foreground "#8B4513") + (:dark :foreground "#DEB887")) + +(define-attribute document-code-block-attribute + (:light :background "#F0F0F0" :foreground "#000000") + (:dark :background "#2F4F4F" :foreground "#E0FFFF")) + +(define-attribute document-inline-code-attribute + (:light :background "#F0F0F0" :foreground "#8B008B") + (:dark :background "#2F4F4F" :foreground "#FF69B4")) + +(define-attribute document-blockquote-attribute + (:light :foreground "#696969") + (:dark :foreground "#D3D3D3")) + +(define-attribute document-table-attribute + (:light :foreground "#000000" :background "#E6E6FA") + (:dark :foreground "#FFFFFF" :background "#4B0082")) + +(define-attribute document-task-list-attribute + (:light :foreground "#228B22") + (:dark :foreground "#98FB98")) + +(define-attribute document-metadata-attribute + (:light :foreground "#4682B4") + (:dark :foreground "#87CEEB")) + (defun attribute-value* (attribute key) (let ((attribute (ensure-attribute attribute nil))) (when attribute @@ -255,5 +320,5 @@ (and (attribute-p attribute) (attribute-value attribute :cursor))) -(defun set-cursor-attribute (attribute) - (setf (attribute-value attribute :cursor) t)) +(defun set-cursor-attribute (attribute &key (value t)) + (setf (attribute-value attribute :cursor) value)) diff --git a/src/buffer/internal/basic.lisp b/src/buffer/internal/basic.lisp index 80c6ab1d9..259be5796 100644 --- a/src/buffer/internal/basic.lisp +++ b/src/buffer/internal/basic.lisp @@ -23,7 +23,7 @@ (defun end-line-p (point) "Return t if POINT is at the end of a line." (= (point-charpos point) - (line-length (point-line point)))) + (line:line-length (point-line point)))) (defun start-buffer-p (point) "Return t if POINT is at the beginning of the buffer." @@ -34,11 +34,11 @@ (point<= (buffer-end-point (point-buffer point)) point)) (defun %move-to-position (point linum line charpos) - (assert (line-alive-p line)) + (assert (line:line-alive-p line)) (assert (<= 0 charpos)) (without-interrupts (point-change-line point linum line) - (setf (point-charpos point) (min (line-length line) charpos))) + (setf (point-charpos point) (min (line:line-length line) charpos))) point) (defun move-point (point new-point) @@ -58,7 +58,7 @@ (defun line-end (point) "Move POINT to the end of the line." (setf (point-charpos point) - (line-length (point-line point))) + (line:line-length (point-line point))) point) (defun buffer-start (point) @@ -76,7 +76,7 @@ If there is no line at the 'n' destination, the position of 'point' is left as i (cond ((plusp n) (do ((i n (1- i)) - (line (point-line point) (line-next line))) + (line (point-line point) (line:line-next line))) ((null line) nil) (when (zerop i) (%move-to-position point (+ (point-linum point) n) @@ -84,7 +84,7 @@ If there is no line at the 'n' destination, the position of 'point' is left as i (return point)))) ((minusp n) (do ((i n (1+ i)) - (line (point-line point) (line-prev line))) + (line (point-line point) (line:line-previous line))) ((null line) nil) (when (zerop i) (%move-to-position point (+ (point-linum point) n) @@ -95,36 +95,34 @@ If there is no line at the 'n' destination, the position of 'point' is left as i (if (< charpos 0) 0 (min charpos - (line-length (point-line point))))) + (line:line-length (point-line point))))) point))) (declaim (inline %character-offset)) (defun %character-offset (point n fn zero-fn) (cond ((zerop n) (when zero-fn (funcall zero-fn))) ((plusp n) - (do ((line (point-line point) (line-next line)) + (do ((line (point-line point) (line:line-next line)) (linum (point-linum point) (1+ linum)) (charpos (point-charpos point) 0)) ((null line) nil) - (let ((w (- (line-length line) charpos))) + (let ((w (- (line:line-length line) charpos))) (when (<= n w) (return (funcall fn linum line (+ charpos n)))) (decf n (1+ w))))) (t (setf n (- n)) - (do* ((line (point-line point) (line-prev line)) + (do* ((line (point-line point) (line:line-previous line)) (linum (point-linum point) (1- linum)) - (charpos (point-charpos point) (and line (line-length line)))) + (charpos (point-charpos point) (and line (line:line-length line)))) ((null line) nil) (when (<= n charpos) (return (funcall fn linum line (- charpos n)))) (decf n (1+ charpos)))))) (defun character-offset (point n) - "If 'point' is a positive number, move it later. If it is a negative number, move it forward. Return the moved 'point'. + "If 'n' is a positive number, move it forward. If it is a negative number, move it backward. Return the moved 'point'. If the 'n' character is beyond the buffer, the position of 'point' is left as it is and NIL is returned." - ;; `point`を`n`が正の数なら後に、負の数なら前に移動し、移動後の`point`を返します。 - ;; `n`文字先がバッファの範囲外なら`point`の位置はそのままでNILを返します (%character-offset point n (lambda (linum line charpos) (%move-to-position point linum line charpos) @@ -138,23 +136,23 @@ Return NIL if the buffer is out of range." (%character-offset point offset (lambda (linum line charpos) (declare (ignore linum)) - (line-char line charpos)) + (line:line-char line charpos)) (lambda () - (line-char (point-line point) + (line:line-char (point-line point) (point-charpos point))))) (defun line-string (point) "Return the string at POINT." - (line-str (point-line point))) + (line:line-string (point-line point))) (defun text-property-at (point prop &optional (offset 0)) "Return the property of 'prop' at the offset position from 'point' to 'offset'." (%character-offset point offset (lambda (linum line charpos) (declare (ignore linum)) - (line-search-property line prop charpos)) + (line:line-search-property line prop charpos)) (lambda () - (line-search-property (point-line point) + (line:line-search-property (point-line point) prop (point-charpos point))))) @@ -166,10 +164,10 @@ The third and fourth arguments PROP and VALUE specify the property to add." (point-buffer end-point))) (%map-region start-point end-point (lambda (line start end) - (line-add-property line + (line:line-add-property line start (if (null end) - (line-length line) + (line:line-length line) end) prop value @@ -183,10 +181,10 @@ The third argument PROP is a property to remove." (point-buffer end-point))) (%map-region start-point end-point (lambda (line start end) - (line-remove-property line + (line:line-remove-property line start (if (null end) - (line-length line) + (line:line-length line) end) prop)))) @@ -222,7 +220,7 @@ If the scan does not stop to the first position of the buffer, or if 'limit-poin (defun insert-character (point char &optional (n 1)) "Insert the character 'char' into 'point' 'n' times." - (loop :repeat n :do (insert-char/point point char)) + (insert-string/point point (str:repeat n (string char))) t) (defun insert-string (point string &rest plist) @@ -276,7 +274,7 @@ Return NIL if the end of the buffer has been reached before deleting 'n' charact (rotatef start end)) (let ((start-line (point-line start)) (end-line (point-line end))) - (loop :for line := start-line :then (line-next line) + (loop :for line := start-line :then (line:line-next line) :for firstp := (eq line start-line) :for lastp := (eq line end-line) :do (funcall function @@ -294,7 +292,7 @@ Return NIL if the end of the buffer has been reached before deleting 'n' charact (%map-region start end (lambda (line start end) (funcall function - (subseq (line-str line) start end) + (line:line-substring line :start start :end end) (not (null end)))))) (defun points-to-string (start-point end-point) @@ -382,9 +380,9 @@ short to reach COLUMN, add spaces/tabs to get there." (defun position-at-point (point) "Return the offset of 'point' from the beginning of the buffer." (let ((offset (point-charpos point))) - (do ((line (line-prev (point-line point)) (line-prev line))) + (do ((line (line:line-previous (point-line point)) (line:line-previous line))) ((null line) (1+ offset)) - (incf offset (1+ (line-length line)))))) + (incf offset (1+ (line:line-length line)))))) (defun move-to-position (point position) "Move 'point' to the offset of 'position' from the beginning of the buffer and return its position. @@ -493,10 +491,10 @@ If 'test' is a function, it takes one of the characters before its position as i (defun insert-buffer (point buffer) "Insert the text in 'buffer' at the position of 'point'. The difference from 'insert-string' is that the text properties in 'buffer' are also reflected." - (loop :for line := (point-line (buffer-start-point buffer)) :then (line-next line) + (loop :for line := (point-line (buffer-start-point buffer)) :then (line:line-next line) :while line - :do (insert-string point (line-str line)) - (setf (line-plist (point-line point)) (line-plist line)) + :do (insert-string point (line:line-string line)) + (setf (line:line-plist (point-line point)) (line:line-plist line)) (insert-character point #\newline))) (defun buffer-text (buffer) diff --git a/src/buffer/internal/buffer-insert.lisp b/src/buffer/internal/buffer-insert.lisp index 3468196cf..57119c075 100644 --- a/src/buffer/internal/buffer-insert.lisp +++ b/src/buffer/internal/buffer-insert.lisp @@ -9,43 +9,38 @@ (define-editor-variable before-change-functions '()) (define-editor-variable after-change-functions '()) -(defun step-on-read-only (point n) - (loop :for line := (point-line point) :then (line-next line) +(defun check-read-only-at-point (point n) + (loop :for line := (point-line point) :then (line:line-next line) :for charpos := (point-charpos point) :then 0 :do (unless line - (return nil)) - (when (line-search-property-range line :read-only charpos (+ charpos n)) - (return t)) - (when (>= 0 (decf n (1+ (- (line-length line) charpos)))) - (return nil)))) - -(defun check-read-only-at-point (point n) - (unless *inhibit-read-only* - (let ((line (point-line point)) - (charpos (point-charpos point))) - (when (if (eql n 0) - (line-search-property line :read-only charpos) - (step-on-read-only point n)) - (error 'read-only-error))))) + (return)) + (when (line:line-search-property-range line :read-only charpos (+ charpos n)) + (error 'read-only-error)) + (when (>= 0 (decf n (1+ (- (line:line-length line) charpos)))) + (return)))) + +(defun call-with-modify-buffer (point n function) + (without-interrupts + (let ((buffer (point-buffer point))) + (unless *inhibit-read-only* + (check-read-only-buffer buffer) + (check-read-only-at-point point n)) + (prog1 (funcall function) + (buffer-modify buffer))))) -(defmacro with-modify-buffer (buffer &body body) - (alexandria:once-only (buffer) - `(without-interrupts - (unless *inhibit-read-only* - (check-read-only-buffer ,buffer)) - (prog1 (progn ,@body) - (buffer-modify ,buffer))))) +(defmacro with-modify-buffer ((point n) &body body) + `(call-with-modify-buffer ,point ,n (lambda () ,@body))) (defun line-next-n (line n) (loop :repeat n - :do (setf line (line-next line))) + :do (setf line (line:line-next line))) line) (defun shift-markers (point offset-line offset-char) (cond ((and (= 0 offset-line) (< 0 offset-char)) (let ((charpos (point-charpos point))) - (dolist (p (line-points (point-line point))) + (dolist (p (line:line-points (point-line point))) (when (etypecase (point-kind p) ((eql :left-inserting) (<= charpos (point-charpos p))) @@ -73,7 +68,7 @@ (> 0 offset-char)) (let ((charpos (point-charpos point)) (n (- offset-char))) - (dolist (p (line-points (point-line point))) + (dolist (p (line:line-points (point-line point))) (when (< charpos (point-charpos p)) (setf (point-charpos p) (if (> charpos (- (point-charpos p) n)) @@ -101,307 +96,114 @@ (t (decf (point-linum p) offset-line))))))))) -(defun %insert-newline/point (buffer line charpos) - (make-line line - (line-next line) - (subseq (line-str line) charpos)) - (line-property-insert-newline line (line-next line) charpos) - (incf (buffer-nlines buffer)) - (setf (line-str line) - (subseq (line-str line) 0 charpos))) - -(defgeneric insert-char/point (point char) - (:method (point char) - (with-modify-buffer (point-buffer point) - (check-read-only-at-point point 0) - (cond - ((char= char #\newline) - (%insert-newline/point (point-buffer point) - (point-line point) - (point-charpos point)) - (shift-markers point 1 0)) - (t - (let ((line (point-line point)) - (charpos (point-charpos point))) - (line-property-insert-pos line charpos 1) - (shift-markers point 0 1) - (setf (line-str line) - (concatenate 'string - (subseq (line-str line) 0 charpos) - (string char) - (subseq (line-str line) charpos)))))) - char))) - -(defun %insert-line-string/point (line charpos string) - (line-property-insert-pos line charpos (length string)) - (setf (line-str line) - (concatenate 'string - (subseq (line-str line) 0 charpos) - string - (subseq (line-str line) charpos)))) - (defgeneric insert-string/point (point string) (:method (point string) (let ((buffer (point-buffer point))) - (with-modify-buffer buffer - (check-read-only-at-point point 0) + (with-modify-buffer (point 0) (loop :with start := 0 :for pos := (position #\newline string :start start) - :for line := (point-line point) :then (line-next line) + :for line := (point-line point) :then (line:line-next line) :for charpos := (point-charpos point) :then 0 :for offset-line :from 0 :do (cond ((null pos) (let ((substr (if (= start 0) string (subseq string start)))) - (%insert-line-string/point line charpos substr) + (line:insert-string line substr charpos) (shift-markers point offset-line (length substr))) (return)) (t (let ((substr (subseq string start pos))) - (%insert-line-string/point line charpos substr) - (%insert-newline/point buffer - line - (+ charpos (length substr))) + (line:insert-string line substr charpos) + (line:insert-newline line (+ charpos (length substr))) + (incf (buffer-nlines buffer)) (setf start (1+ pos)))))))) string)) -(defun %delete-line-between/point (point start end line) - (declare (special killring-stream)) - (line-property-delete-pos (point-line point) - (point-charpos point) - (- end start)) - (write-string (line-str line) killring-stream - :start start - :end end) - (setf (line-str line) - (concatenate 'string - (subseq (line-str line) 0 start) - (subseq (line-str line) end)))) - -(defun %delete-line-eol/point (point start line) - (declare (special killring-stream)) - (line-property-delete-line (point-line point) (point-charpos point)) - (write-string (line-str line) killring-stream :start start) - (setf (line-str line) - (subseq (line-str line) 0 start))) - -(defun %delete-line/point (point start line) - (declare (special killring-stream buffer n)) - (line-property-delete-line (point-line point) (point-charpos point)) - (line-merge (point-line point) (line-next (point-line point)) start) - (write-string (line-str line) killring-stream :start start) - (write-char #\newline killring-stream) - (decf n (1+ (- (line-length line) start))) - (decf (buffer-nlines buffer)) - (setf (line-str line) - (concatenate 'string - (subseq (line-str line) 0 start) - (line-str (line-next line)))) - (line-free (line-next line))) - -;;TODO: ABCL complains about n that is not bound -#+abcl -(defparameter n nil) - -(defgeneric delete-char/point (point n) - (:method (point n) - (declare (special n)) - (with-modify-buffer (point-buffer point) - (check-read-only-at-point point n) +(defgeneric delete-char/point (point remaining-deletions) + (:method (point remaining-deletions) + (with-modify-buffer (point remaining-deletions) (with-output-to-string (killring-stream) - (declare (special killring-stream)) (let ((charpos (point-charpos point)) - (buffer (point-buffer point)) (line (point-line point)) (offset-line 0)) - (declare (special buffer)) - (loop :while (plusp n) - :for eolp := (> n (- (line-length line) charpos)) + (loop :while (plusp remaining-deletions) + :for eolp := (> remaining-deletions (- (line:line-length line) charpos)) :do (cond ((not eolp) - (%delete-line-between/point point charpos (+ charpos n) line) - (shift-markers point offset-line (- n)) + (let ((end (+ charpos remaining-deletions))) + (write-string (line:line-substring line :start charpos :end end) + killring-stream) + (line:delete-region line :start charpos :end end)) + (shift-markers point offset-line (- remaining-deletions)) (return)) - ((null (line-next line)) - (%delete-line-eol/point point charpos line) - (shift-markers point offset-line (- charpos (line-length line))) + ((null (line:line-next line)) + (let ((offset (- charpos (line:line-length line)))) + (write-string (line:line-substring line :start charpos) killring-stream) + (line:delete-region line :start charpos) + (shift-markers point offset-line offset)) (return)) (t - (%delete-line/point point charpos line))) + (decf (buffer-nlines (point-buffer point))) + (decf remaining-deletions (1+ (- (line:line-length line) charpos))) + (write-line (line:line-substring line :start charpos) killring-stream) + (line:merge-with-next-line line :start charpos))) (decf offset-line) :finally (shift-markers point offset-line 0))))))) -(declaim (inline call-before-change-functions - call-after-change-functions)) - -(defun call-before-change-functions (buffer start arg) +(defun call-before-change-functions (point arg) (unless *inhibit-modification-hooks* - (run-hooks (make-per-buffer-hook :var 'before-change-functions :buffer buffer) - start arg))) + (run-hooks (make-per-buffer-hook :var 'before-change-functions :buffer (point-buffer point)) + point arg))) (defun call-after-change-functions (buffer start end old-len) (unless *inhibit-modification-hooks* (run-hooks (make-per-buffer-hook :var 'after-change-functions :buffer buffer) start end old-len))) -(defmacro insert/after-change-function (point arg) - `(if (and (not *inhibit-modification-hooks*) - (or (variable-value 'after-change-functions) - (variable-value 'after-change-functions :global))) - (with-point ((start ,point)) - (prog1 (call-next-method) - (with-point ((end start)) - (character-offset end ,arg) - (call-after-change-functions (point-buffer ,point) start end 0)))) - (call-next-method))) - -(defmacro delete/after-change-function (point) - `(if (and (not *inhibit-modification-hooks*) - (or (variable-value 'after-change-functions) - (variable-value 'after-change-functions :global))) - (let ((string (call-next-method))) - (with-point ((start ,point) - (end ,point)) - (call-after-change-functions (point-buffer ,point) start end (length string))) - string) - (call-next-method))) - -(defmethod insert-char/point :around (point char) - (call-before-change-functions (point-buffer point) point char) - (if (not (buffer-enable-undo-p (point-buffer point))) - (insert/after-change-function point 1) - (let ((linum (line-number-at-point point)) - (charpos (point-charpos point))) - (prog1 (insert/after-change-function point 1) - (without-interrupts - (push-undo (point-buffer point) - (make-edit :insert-char linum charpos char))))))) +(defun need-to-call-after-change-functions-p () + (and (not *inhibit-modification-hooks*) + (or (variable-value 'after-change-functions) + (variable-value 'after-change-functions :global)))) + +(defun insert/after-change-function (point arg call-next-method) + (if (need-to-call-after-change-functions-p) + (with-point ((start point)) + (prog1 (funcall call-next-method) + (with-point ((end start)) + (character-offset end arg) + (call-after-change-functions (point-buffer point) start end 0)))) + (funcall call-next-method))) + +(defun delete/after-change-function (point call-next-method) + (if (need-to-call-after-change-functions-p) + (let ((string (funcall call-next-method))) + (with-point ((start point) + (end point)) + (call-after-change-functions (point-buffer point) start end (length string))) + string) + (funcall call-next-method))) (defmethod insert-string/point :around (point string) - (call-before-change-functions (point-buffer point) point string) - (if (not (buffer-enable-undo-p (point-buffer point))) - (insert/after-change-function point (length string)) - (let ((linum (line-number-at-point point)) - (charpos (point-charpos point))) - (prog1 (insert/after-change-function point (length string)) - (without-interrupts - (push-undo (point-buffer point) - (make-edit :insert-string linum charpos string))))))) - -(defmethod delete-char/point :around (point n) - (call-before-change-functions (point-buffer point) point n) - (if (not (buffer-enable-undo-p (point-buffer point))) - (delete/after-change-function point) - (let ((linum (line-number-at-point point)) - (charpos (point-charpos point)) - (string (delete/after-change-function point))) - (without-interrupts - (push-undo (point-buffer point) - (make-edit :delete-char linum charpos string))) - string))) - - -;;; undo/redo -(defparameter *undo-modes* '(:edit :undo :redo)) -(defvar *undo-mode* :edit) - -(defun buffer-enable-undo-p (&optional (buffer (current-buffer))) - "Returns T if undo is enabled for `buffer`, otherwise returns NIL." - (buffer-%enable-undo-p buffer)) - -(defun buffer-enable-undo (buffer) - "Enables undo for `buffer`." - (setf (buffer-%enable-undo-p buffer) t) - nil) - -(defun buffer-disable-undo (buffer) - "Disables undo for `buffer` and remove all undo information." - (setf (buffer-%enable-undo-p buffer) nil) - (setf (buffer-edit-history buffer) (make-array 0 :adjustable t :fill-pointer 0)) - (setf (buffer-redo-stack buffer) nil) - nil) - -(defun buffer-enable-undo-boundary-p (&optional (buffer (current-buffer))) - (buffer-%enable-undo-boundary-p buffer)) - -(defun buffer-enable-undo-boundary (buffer) - (setf (buffer-%enable-undo-boundary-p buffer) t) - nil) - -(defun buffer-disable-undo-boundary (buffer) - (setf (buffer-%enable-undo-boundary-p buffer) nil) - nil) - -(defun buffer-modify (buffer) - (ecase *undo-mode* - ((:edit :redo) - (incf (buffer-%modified-p buffer))) - ((:undo) - (decf (buffer-%modified-p buffer)))) - (buffer-mark-cancel buffer)) - -(defun push-undo-stack (buffer elt) - (vector-push-extend elt (buffer-edit-history buffer))) - -(defun push-redo-stack (buffer elt) - (push elt (buffer-redo-stack buffer))) - -(defun push-undo (buffer edit) - (when (buffer-enable-undo-p buffer) - (ecase *undo-mode* - (:edit - (push-undo-stack buffer edit) - (setf (buffer-redo-stack buffer) nil)) - (:redo - (push-undo-stack buffer edit)) - (:undo - (push-redo-stack buffer edit))))) - -(defun buffer-undo-1 (point) - (let* ((buffer (point-buffer point)) - (edit-history (buffer-edit-history buffer)) - (elt (and (< 0 (length edit-history)) (vector-pop edit-history)))) - (when elt - (let ((*undo-mode* :undo)) - (unless (eq elt :separator) - (apply-inverse-edit elt point)))))) - -(defun buffer-undo (point) + (call-before-change-functions point string) (let ((buffer (point-buffer point))) - (push :separator (buffer-redo-stack buffer)) - (when (eq :separator (last-edit-history buffer)) - (vector-pop (buffer-edit-history buffer))) - (let ((result0 nil)) - (loop :for result := (buffer-undo-1 point) - :while result - :do (setf result0 result)) - (unless result0 - (assert (eq :separator (car (buffer-redo-stack buffer)))) - (pop (buffer-redo-stack buffer))) - result0))) - -(defun buffer-redo-1 (point) - (let* ((buffer (point-buffer point)) - (elt (pop (buffer-redo-stack buffer)))) - (when elt - (let ((*undo-mode* :redo)) - (unless (eq elt :separator) - (apply-inverse-edit elt point)))))) - -(defun buffer-redo (point) + (if (not (buffer-enable-undo-p buffer)) + (insert/after-change-function point (length string) #'call-next-method) + (let ((position (position-at-point point))) + (prog1 (insert/after-change-function point (length string) #'call-next-method) + (let ((edit (make-edit :insert-string position string))) + (if (inhibit-undo-p) + (recompute-undo-position-offset buffer edit) + (push-undo buffer edit)))))))) + +(defmethod delete-char/point :around (point remaining-deletions) + (call-before-change-functions point remaining-deletions) (let ((buffer (point-buffer point))) - (vector-push-extend :separator (buffer-edit-history buffer)) - (let ((result0 nil)) - (loop :for result := (buffer-redo-1 point) - :while result - :do (setf result0 result)) - (unless result0 - (assert (eq :separator - (last-edit-history buffer))) - (vector-pop (buffer-edit-history buffer))) - result0))) - -(defun buffer-undo-boundary (&optional (buffer (current-buffer))) - (when (buffer-enable-undo-boundary-p) - (unless (eq :separator (last-edit-history buffer)) - (vector-push-extend :separator (buffer-edit-history buffer))))) + (if (not (buffer-enable-undo-p buffer)) + (delete/after-change-function point #'call-next-method) + (let* ((position (position-at-point point)) + (string (delete/after-change-function point #'call-next-method)) + (edit (make-edit :delete-string position string))) + (if (inhibit-undo-p) + (recompute-undo-position-offset buffer edit) + (push-undo buffer edit)) + string)))) diff --git a/src/buffer/internal/buffer.lisp b/src/buffer/internal/buffer.lisp index 52030b28d..1f4a4446e 100644 --- a/src/buffer/internal/buffer.lisp +++ b/src/buffer/internal/buffer.lisp @@ -164,7 +164,7 @@ Options that can be specified by arguments are ignored if `temporary` is NIL and :%enable-undo-p enable-undo-p :temporary temporary :syntax-table syntax-table))) - (let* ((temp-point (make-point buffer 1 (make-empty-line) 0 :kind :temporary)) + (let* ((temp-point (make-point buffer 1 (line:make-empty-line) 0 :kind :temporary)) (start-point (make-buffer-start-point temp-point)) (end-point (make-buffer-end-point temp-point)) (point (make-buffer-point temp-point))) diff --git a/src/buffer/internal/check-corruption.lisp b/src/buffer/internal/check-corruption.lisp index ff4bfeb8d..b8b934250 100644 --- a/src/buffer/internal/check-corruption.lisp +++ b/src/buffer/internal/check-corruption.lisp @@ -1,21 +1,24 @@ (in-package :lem/buffer/internal) +(define-condition corruption-warning (simple-warning) ()) + (defmacro debug-assert (form &rest args) `(unless ,form - (log:error "assertion failed" ,form ,@args))) + (log:error "assertion failed" ,form ,@args) + (warn 'corruption-warning))) (defun check-line-corruption (line line-number buffer) - (check-type line line) + (check-type line line:line) (check-type buffer buffer) - (when (line-prev line) - (debug-assert (eq line (line-next (line-prev line))) + (when (line:line-previous line) + (debug-assert (eq line (line:line-next (line:line-previous line))) "line.prev.next is not line" line)) - (when (line-next line) - (debug-assert (eq line (line-prev (line-next line))) + (when (line:line-next line) + (debug-assert (eq line (line:line-previous (line:line-next line))) "line.next.prev is not line" line)) - (dolist (point (line-points line)) + (dolist (point (line:line-points line)) (debug-assert (eq buffer (point-buffer point)) "point.buffer is not buffer" point) @@ -30,7 +33,7 @@ (defun check-lines-corruption (first-line buffer) (loop :for prev-line := nil :then line - :for line := first-line :then (line-next line) + :for line := first-line :then (line:line-next line) :for line-number :from 1 :while line :do (check-line-corruption line line-number buffer) @@ -38,9 +41,9 @@ (defun check-buffer-points-corruption (buffer) (let ((collected-buffer-points - (loop :for line := (point-line (buffer-start-point buffer)) :then (line-next line) + (loop :for line := (point-line (buffer-start-point buffer)) :then (line:line-next line) :while line - :append (line-points line)))) + :append (line:line-points line)))) (debug-assert (alexandria:set-equal (buffer-points buffer) collected-buffer-points :test #'eq) @@ -50,13 +53,13 @@ (defun check-buffer-corruption (buffer) (check-type buffer buffer) (let ((first-line (point-line (buffer-start-point buffer)))) - (debug-assert (null (line-prev first-line))) + (debug-assert (null (line:line-previous first-line))) (let ((last-line (check-lines-corruption first-line buffer))) - (debug-assert (null (line-next last-line))) + (debug-assert (null (line:line-next last-line))) (debug-assert (eq last-line (point-line (buffer-end-point buffer)))) (debug-assert (member (buffer-end-point buffer) - (line-points last-line))))) + (line:line-points last-line))))) (check-buffer-points-corruption buffer) (debug-assert (member (buffer-point buffer) (buffer-points buffer))) (debug-assert (member (buffer-start-point buffer) (buffer-points buffer))) @@ -64,7 +67,12 @@ (dolist (point (buffer-points buffer)) (debug-assert (point<= (buffer-start-point buffer) point - (buffer-end-point buffer))))) + (buffer-end-point buffer)))) + (let ((end-point (buffer-end-point buffer))) + (debug-assert (= (buffer-nlines buffer) + (line-number-at-point end-point))) + (debug-assert (= (lem/buffer/line:line-length (point-line end-point)) + (point-charpos end-point))))) (defun check-all-buffers-corruption () (dolist (buffer (buffer-list)) diff --git a/src/buffer/internal/edit.lisp b/src/buffer/internal/edit.lisp index fcbf35a6c..a9574b3d7 100644 --- a/src/buffer/internal/edit.lisp +++ b/src/buffer/internal/edit.lisp @@ -1,36 +1,50 @@ (in-package :lem/buffer/internal) -(defun make-edit (kind linum charpos value) - (list kind linum charpos value)) +(deftype edit-kind () + '(member :insert-string :delete-string)) -(defun %apply-edit (point kind linum charpos value) - (ecase kind - ((:insert-char) - (move-to-line point linum) - (line-offset point 0 charpos) - (insert-char/point point value)) +(defstruct (edit (:constructor make-edit (kind position string))) + (kind (alexandria:required-argument :kind) + :type edit-kind + :read-only t) + (position (alexandria:required-argument :position) + :type (integer 0 *)) + (string (alexandria:required-argument :string) + :type string + :read-only t)) + +(defun apply-edit (point edit) + (ecase (edit-kind edit) ((:insert-string) - (move-to-line point linum) - (line-offset point 0 charpos) + (move-to-position point (edit-position edit)) (with-point ((p point)) - (insert-string/point point value) + (insert-string/point point (edit-string edit)) (move-point point p))) - ((:delete-char) - (move-to-line point linum) - (line-offset point 0 charpos) - (delete-char/point point value)))) + ((:delete-string) + (move-to-position point (edit-position edit)) + (delete-char/point point (length (edit-string edit)))))) + +(defun apply-inverse-edit (point edit) + (ecase (edit-kind edit) + ((:insert-string) + (apply-edit point + (make-edit :delete-string + (edit-position edit) + (edit-string edit)))) + ((:delete-string) + (apply-edit point + (make-edit :insert-string + (edit-position edit) + (edit-string edit)))))) -(defun apply-inverse-edit (edit point) - (destructuring-bind (kind linum charpos value) edit - (ecase kind - ((:insert-char) - (%apply-edit point :delete-char linum charpos 1)) - ((:insert-string) - (%apply-edit point :delete-char linum charpos (length value))) - ((:delete-char) - (let ((charp (= 1 (length value)))) - (%apply-edit point - (if charp :insert-char :insert-string) - linum - charpos - (if charp (aref value 0) value))))))) +(defun compute-edit-offset (dest src) + (ecase (edit-kind src) + ((:insert-string) + (when (<= (edit-position src) (edit-position dest)) + (incf (edit-position dest) (length (edit-string src))))) + ((:delete-string) + (when (< (edit-position src) + (edit-position dest)) + (decf (edit-position dest) (length (edit-string src))) + (when (< (edit-position dest) (edit-position src)) + (setf (edit-position dest) (edit-position src))))))) diff --git a/src/buffer/internal/point.lisp b/src/buffer/internal/point.lisp index 9e6152d55..ffbf65e90 100644 --- a/src/buffer/internal/point.lisp +++ b/src/buffer/internal/point.lisp @@ -1,5 +1,8 @@ (in-package :lem/buffer/internal) +(deftype point-kind () + '(member :temporary :left-inserting :right-inserting)) + (defclass point () ((buffer :reader point-buffer @@ -15,7 +18,7 @@ :type fixnum) (kind :reader point-kind - :type (member :temporary :left-inserting :right-inserting))) + :type point-kind)) (:documentation "`point` is an object that points to the position of the text in the buffer. It has a `buffer` slot, a `line` number, and `charpos` is an offset from the beginning of the line, starting at zero. @@ -43,7 +46,7 @@ When using `:left-inserting` or `:right-inserting`, you must explicitly delete t (format stream "(~D, ~D) ~S" (point-linum object) (point-charpos object) - (line-str (point-line object))))) + (line:line-string (point-line object))))) (defun pointp (x) "Returns T if `x` is a `point`, otherwise returns NIL." @@ -53,8 +56,8 @@ When using `:left-inserting` or `:right-inserting`, you must explicitly delete t (point &key (buffer (alexandria:required-argument :buffer)) (linum (alexandria:required-argument :linum)) (line (alexandria:required-argument :line)) - (charpos (alexandria:required-argument :line)) - (kind (alexandria:required-argument :line))) + (charpos (alexandria:required-argument :charpos)) + (kind (alexandria:required-argument :kind))) (setf (slot-value point 'buffer) buffer (slot-value point 'linum) linum (slot-value point 'line) line @@ -64,11 +67,11 @@ When using `:left-inserting` or `:right-inserting`, you must explicitly delete t (defun initialize-point (point kind) (unless (eq :temporary kind) - (push point (line-points (point-line point))) + (push point (line:line-points (point-line point))) (push point (buffer-points (point-buffer point))))) (defun make-point (buffer linum line charpos &key (kind :right-inserting)) - (check-type kind (member :temporary :left-inserting :right-inserting)) + (check-type kind point-kind) (let ((point (make-instance 'point))) (initialize-point-slot-values point :buffer buffer @@ -80,7 +83,7 @@ When using `:left-inserting` or `:right-inserting`, you must explicitly delete t point)) (defmethod copy-point-using-class ((point point) from-point kind) - (check-type kind (member :temporary :left-inserting :right-inserting)) + (check-type kind point-kind) (initialize-point-slot-values point :buffer (point-buffer from-point) :linum (point-linum from-point) @@ -102,8 +105,8 @@ If omitted, is copied from `point`." "Delete `point`. If `point-kind` is `:temporary` this is unnecessary." (unless (point-temporary-p point) - (setf (line-points (point-line point)) - (delete point (line-points (point-line point)))) + (setf (line:line-points (point-line point)) + (delete point (line:line-points (point-line point)))) (let ((buffer (point-buffer point))) (setf (buffer-points buffer) (delete point (buffer-points buffer)))) @@ -111,22 +114,22 @@ If `point-kind` is `:temporary` this is unnecessary." (defun alive-point-p (point) (alexandria:when-let (line (point-line point)) - (line-alive-p line))) + (line:line-alive-p line))) (defun point-change-line (point new-linum new-line) (unless (point-temporary-p point) (let ((old-line (point-line point))) - (if (line-alive-p old-line) - (do ((scan (line-points old-line) (cdr scan)) + (if (line:line-alive-p old-line) + (do ((scan (line:line-points old-line) (cdr scan)) (prev nil scan)) ((eq (car scan) point) (if prev (setf (cdr prev) (cdr scan)) - (setf (line-points old-line) (cdr scan))) - (setf (cdr scan) (line-points new-line) - (line-points new-line) scan)) + (setf (line:line-points old-line) (cdr scan))) + (setf (cdr scan) (line:line-points new-line) + (line:line-points new-line) scan)) (assert (not (null scan)))) - (push point (line-points new-line))))) + (push point (line:line-points new-line))))) (setf (point-linum point) new-linum) (setf (point-line point) new-line)) @@ -274,4 +277,4 @@ Example: ;; TODO: delete this ugly function (defun get-string-and-attributes-at-point (point) - (line-string/attributes (point-line point))) + (line:line-string/attributes (point-line point))) diff --git a/src/buffer/internal/tmlanguage.lisp b/src/buffer/internal/tmlanguage.lisp index 3cb1d42aa..7b720b0fb 100644 --- a/src/buffer/internal/tmlanguage.lisp +++ b/src/buffer/internal/tmlanguage.lisp @@ -122,10 +122,10 @@ (defun set-syntax-context (line x) - (setf (line-syntax-context line) x)) + (setf (line:line-syntax-context line) x)) (defun get-syntax-context (line) - (line-syntax-context line)) + (line:line-syntax-context line)) (defun tm-get-repository (name) (gethash name (tmlanguage-repository (current-syntax-parser)))) @@ -223,7 +223,7 @@ (tm-patterns (tm-scan-line point capture start end)) (otherwise - (line-add-property (point-line point) start end :attribute capture nil)))) + (line:line-add-property (point-line point) start end :attribute capture nil)))) (defun tm-apply-captures (point result captures) (when (and captures (< 0 (length captures))) @@ -242,7 +242,7 @@ (defun tm-apply-content-name (rule point start end contp) (alexandria:when-let (content-name (tm-region-content-name rule)) - (line-add-property (point-line point) start end + (line:line-add-property (point-line point) start end :attribute content-name contp))) @@ -300,16 +300,16 @@ (setf best (tm-get-best-result best end-result)) (loop (cond ((null best) - (line-add-property (point-line point) start1 (line-length (point-line point)) + (line:line-add-property (point-line point) start1 (line:line-length (point-line point)) :attribute (tm-rule-name rule) t) (tm-apply-begin-captures rule point begin-result start-line-p) - (tm-apply-content-name rule point start2 (line-length (point-line point)) t) + (tm-apply-content-name rule point start2 (line:line-length (point-line point)) t) (set-syntax-context (point-line point) (cons rule begin-result)) (line-end point) (return)) ((and best end-result (tm-result= best end-result)) - (line-add-property (point-line point) start1 (tm-result-end end-result) + (line:line-add-property (point-line point) start1 (tm-result-end end-result) :attribute (tm-rule-name rule) nil) (tm-apply-begin-captures rule point begin-result start-line-p) @@ -357,13 +357,13 @@ (tm-patterns (tm-scan-line point capture start end)) (otherwise - (line-add-property (point-line point) start end :attribute capture nil)))) + (line:line-add-property (point-line point) start end :attribute capture nil)))) (defun tm-apply-match (rule point result) (let ((start (tm-result-start result)) (end (tm-result-end result)) (captures (tm-match-captures rule))) - (line-add-property (point-line point) start end :attribute (tm-rule-name rule) nil) + (line:line-add-property (point-line point) start end :attribute (tm-rule-name rule) nil) (tm-apply-captures point result captures) (cond ((tm-match-move-action rule) (line-offset point 0 start) @@ -382,7 +382,7 @@ (defun tm-continue-prev-line (point) (let* ((line (point-line point)) - (prev (line-prev line)) + (prev (line:line-previous line)) (context (and prev (get-syntax-context prev))) (rule (alexandria:ensure-car context))) (cond ((null rule) @@ -399,8 +399,8 @@ (when goal (move-point point goal))))) (t - (line-add-property (point-line point) - 0 (line-length line) + (line:line-add-property (point-line point) + 0 (line:line-length line) :attribute (tm-rule-name rule) t) (line-end point)))) @@ -429,7 +429,7 @@ (defun tm-syntax-scan-region (start end) (loop - (line-clear-property (point-line start) :attribute) + (line:line-clear-property (point-line start) :attribute) (unless (tm-syntax-scan-line start) (return start)) (when (point<= end start) diff --git a/src/buffer/internal/undo.lisp b/src/buffer/internal/undo.lisp new file mode 100644 index 000000000..0655d44be --- /dev/null +++ b/src/buffer/internal/undo.lisp @@ -0,0 +1,122 @@ +(in-package :lem/buffer/internal) + +(defparameter *undo-modes* '(:edit :undo :redo)) +(defvar *undo-mode* :edit) + +(defvar *inhibit-undo* nil) + +(defun inhibit-undo-p () + *inhibit-undo*) + +(defmacro with-inhibit-undo (() &body body) + `(let ((*inhibit-undo* t)) + ,@body)) + +(defun buffer-enable-undo-p (&optional (buffer (current-buffer))) + "Returns T if undo is enabled for `buffer`, otherwise returns NIL." + (and (buffer-%enable-undo-p buffer) (not *inhibit-undo*))) + +(defun buffer-enable-undo (buffer) + "Enables undo for `buffer`." + (setf (buffer-%enable-undo-p buffer) t) + nil) + +(defun buffer-disable-undo (buffer) + "Disables undo for `buffer` and remove all undo information." + (setf (buffer-%enable-undo-p buffer) nil) + (setf (buffer-edit-history buffer) (make-array 0 :adjustable t :fill-pointer 0)) + (setf (buffer-redo-stack buffer) nil) + nil) + +(defun buffer-enable-undo-boundary-p (&optional (buffer (current-buffer))) + (buffer-%enable-undo-boundary-p buffer)) + +(defun buffer-enable-undo-boundary (buffer) + (setf (buffer-%enable-undo-boundary-p buffer) t) + nil) + +(defun buffer-disable-undo-boundary (buffer) + (setf (buffer-%enable-undo-boundary-p buffer) nil) + nil) + +(defun buffer-undo-boundary (&optional (buffer (current-buffer))) + (when (buffer-enable-undo-boundary-p) + (unless (eq :separator (last-edit-history buffer)) + (vector-push-extend :separator (buffer-edit-history buffer))))) + +(defun buffer-modify (buffer) + (ecase *undo-mode* + ((:edit :redo) + (incf (buffer-%modified-p buffer))) + ((:undo) + (decf (buffer-%modified-p buffer)))) + (buffer-mark-cancel buffer)) + +(defun push-undo-stack (buffer elt) + (vector-push-extend elt (buffer-edit-history buffer))) + +(defun push-redo-stack (buffer elt) + (push elt (buffer-redo-stack buffer))) + +(defun push-undo (buffer edit) + (when (buffer-enable-undo-p buffer) + (ecase *undo-mode* + (:edit + (push-undo-stack buffer edit) + (setf (buffer-redo-stack buffer) nil)) + (:redo + (push-undo-stack buffer edit)) + (:undo + (push-redo-stack buffer edit))))) + +(defun buffer-undo-1 (point) + (let* ((buffer (point-buffer point)) + (edit-history (buffer-edit-history buffer)) + (elt (and (< 0 (length edit-history)) (vector-pop edit-history)))) + (when elt + (let ((*undo-mode* :undo)) + (unless (eq elt :separator) + (apply-inverse-edit point elt)))))) + +(defun buffer-undo (point) + (let ((buffer (point-buffer point))) + (push :separator (buffer-redo-stack buffer)) + (when (eq :separator (last-edit-history buffer)) + (vector-pop (buffer-edit-history buffer))) + (let ((result0 nil)) + (loop :for result := (buffer-undo-1 point) + :while result + :do (setf result0 result)) + (unless result0 + (assert (eq :separator (car (buffer-redo-stack buffer)))) + (pop (buffer-redo-stack buffer))) + result0))) + +(defun buffer-redo-1 (point) + (let* ((buffer (point-buffer point)) + (elt (pop (buffer-redo-stack buffer)))) + (when elt + (let ((*undo-mode* :redo)) + (unless (eq elt :separator) + (apply-inverse-edit point elt)))))) + +(defun buffer-redo (point) + (let ((buffer (point-buffer point))) + (vector-push-extend :separator (buffer-edit-history buffer)) + (let ((result0 nil)) + (loop :for result := (buffer-redo-1 point) + :while result + :do (setf result0 result)) + (unless result0 + (assert (eq :separator + (last-edit-history buffer))) + (vector-pop (buffer-edit-history buffer))) + result0))) + +(defun recompute-undo-position-offset (buffer edit) + (loop :for edit-1 :across (buffer-edit-history buffer) + :do (unless (eq edit-1 :separator) + (compute-edit-offset edit-1 edit))) + (loop :for edit-1 :in (buffer-redo-stack buffer) + :do (unless (eq edit-1 :separator) + (compute-edit-offset edit-1 edit)))) diff --git a/src/buffer/interrupt.lisp b/src/buffer/interrupt.lisp index 0a25b3c75..5526afce2 100644 --- a/src/buffer/interrupt.lisp +++ b/src/buffer/interrupt.lisp @@ -23,7 +23,7 @@ (setf *interrupted* nil) (error 'lem/buffer/errors:editor-interrupt))))))) -;; 別のスレッドから(bt:interrupt-thread thread #'interrupt)で使う関数 +;; 別のスレッドから(bt2:interrupt-thread thread #'interrupt)で使う関数 (defun interrupt (&optional force) (cond (force diff --git a/src/buffer/line.lisp b/src/buffer/line.lisp index e3912e64a..ba46e72a5 100644 --- a/src/buffer/line.lisp +++ b/src/buffer/line.lisp @@ -4,14 +4,15 @@ :content-string :content-attributes :line - :line-prev + :line-previous :line-next - :line-str + :line-string :line-plist :line-syntax-context :line-points :make-line :make-empty-line + :line-free :line-alive-p :line-char :line-length @@ -29,54 +30,88 @@ :line-search-property :line-search-property-range :line-property-insert-pos - :line-property-insert-newline - :line-property-delete-pos - :line-property-delete-line + :line-delete-property-region :line-string/attributes - :line-free)) + :line-substring + :insert-string + :insert-newline + :delete-region + :merge-with-next-line)) (in-package :lem/buffer/line) (defstruct content string attributes) -(defstruct (line (:constructor %make-line)) - prev - next - str - plist - syntax-context - points) +(defclass line () + ((previous + :initarg :previous + :initform nil + :accessor line-previous) + (next + :initarg :next + :initform nil + :accessor line-next) + (string + :initarg :string + :initform nil + :reader line-string + :writer set-line-string) + (plist + :initarg :plist + :initform nil + :accessor line-plist) + (syntax-context + :initarg :syntax-context + :initform nil + :accessor line-syntax-context) + (points + :initarg :points + :initform nil + :accessor line-points))) (defmethod print-object ((object line) stream) (print-unreadable-object (object stream :identity t :type t) (format stream "string: ~S, plist: ~S" - (line-str object) + (line-string object) (line-plist object)))) -(defun make-line (prev next str) - (let ((line (%make-line :next next - :prev prev - :str str))) +(defun make-line (previous next string) + (let ((line (make-instance 'line + :next next + :previous previous + :string string))) (when next - (setf (line-prev next) line)) - (when prev - (setf (line-next prev) line)) + (setf (line-previous next) line)) + (when previous + (setf (line-next previous) line)) line)) (defun make-empty-line () (make-line nil nil "")) +(defun line-free (line) + (when (line-previous line) + (setf (line-next (line-previous line)) + (line-next line))) + (when (line-next line) + (setf (line-previous (line-next line)) + (line-previous line))) + (setf (line-previous line) nil) + (setf (line-next line) nil) + (setf (line-points line) nil) + (set-line-string nil line)) + (defun line-alive-p (line) - (not (null (line-str line)))) + (not (null (line-string line)))) (defun line-char (line i) (if (= i (line-length line)) #\newline - (char (line-str line) i))) + (char (line-string line) i))) (defun line-length (line) - (length (line-str line))) + (length (line-string line))) (defun remove-elements (elements start end) (iter:iter (iter:for (start1 end1 value1) iter:in elements) @@ -143,7 +178,7 @@ (f plist2)) new-plist)) -(defun line-merge (curr-line next-line pos) +(defun line-merge-plist (curr-line next-line pos) (setf (line-plist curr-line) (merge-plist (line-plist curr-line) @@ -230,45 +265,36 @@ (setf (getf new-plist (car plist-rest)) new-values)))) (setf (line-plist next-line) new-plist))) -(defun line-property-delete-pos (line pos n) +(defun line-delete-property-region (line start &optional end) + (unless end (setf end (line-length line))) + (assert (<= start end)) (loop :for plist-rest :on (line-plist line) :by #'cddr :do (setf (cadr plist-rest) (loop :for elt :in (cadr plist-rest) - :for (start end value) := elt + :for (start1 end1 value) := elt - :if (<= pos start end (+ pos n -1)) + :if (<= start start1 end1 (1- end)) :do (progn) - :else :if (<= pos (+ pos n) start) - :collect (list (- start n) (- end n) value) + :else :if (<= start end start1) + :collect (list (- start1 (- end start)) + (- end1 (- end start)) + value) - :else :if (< pos start (+ pos n)) - :collect (list pos (- end n) value) + :else :if (< start start1 end) + :collect (list start (- end1 (- end start)) value) - :else :if (<= start pos (+ pos n) end) - :collect (list start (- end n) value) + :else :if (<= start1 start end end1) + :collect (list start1 (- end1 (- end start)) value) - :else :if (<= start pos end (+ pos n)) - :collect (list start pos value) + :else :if (<= start1 start end1 end) + :collect (list start1 start value) :else :collect elt)))) -(defun line-property-delete-line (line pos) - (loop :for plist-rest :on (line-plist line) :by #'cddr - :do (setf (cadr plist-rest) - (loop :for elt :in (cadr plist-rest) - :for (start end value) := elt - :if (<= pos start) - :do (progn) - :else :if (<= pos end) - :collect (list start pos value) - :else - :collect elt - )))) - (defun line-string/attributes (line) - (cons (line-str line) + (cons (line-string line) (alexandria:if-let (sticky-attribute (getf (line-plist line) :sticky-attribute)) (loop :with attributes := (getf (line-plist line) :attribute) :for (start end value contp) :in sticky-attribute @@ -276,14 +302,40 @@ :finally (return attributes)) (getf (line-plist line) :attribute)))) -(defun line-free (line) - (when (line-prev line) - (setf (line-next (line-prev line)) - (line-next line))) - (when (line-next line) - (setf (line-prev (line-next line)) - (line-prev line))) - (setf (line-prev line) nil - (line-next line) nil - (line-str line) nil - (line-points line) nil)) +(defun line-substring (line &key (start 0) end) + (cond ((and (= start 0) (or (null end) (= end (line-length line)))) + (line-string line)) + (t + (subseq (line-string line) start end)))) + +(defun insert-string (line string index) + (line-property-insert-pos line index (length string)) + (set-line-string (concatenate 'string + (line-substring line :start 0 :end index) + string + (line-substring line :start index)) + line)) + +(defun insert-newline (line position) + (let ((before-string (line-substring line :start 0 :end position)) + (after-string (line-substring line :start position))) + (set-line-string before-string line) + (let ((next (make-line line (line-next line) after-string))) + (line-property-insert-newline line next position)))) + +(defun delete-region (line &key start end) + (line-delete-property-region line start end) + (set-line-string (concatenate 'string + (line-substring line :start 0 :end start) + (line-substring line :start (or end (line-length line)))) + line)) + +(defun merge-with-next-line (line &key (start 0)) + (assert (line-next line)) + (line-delete-property-region line start) + (line-merge-plist line (line-next line) start) + (set-line-string (concatenate 'string + (line-substring line :start 0 :end start) + (line-string (line-next line))) + line) + (line-free (line-next line))) diff --git a/src/buffer/package.lisp b/src/buffer/package.lisp index 8b727af05..1398028ca 100644 --- a/src/buffer/package.lisp +++ b/src/buffer/package.lisp @@ -1,13 +1,13 @@ -(defpackage :lem/buffer/fundamental-mode +(uiop:define-package :lem/buffer/fundamental-mode (:export :fundamental-mode)) (uiop:define-package :lem/buffer/internal (:use :cl - :lem/buffer/line :lem/common/utils :lem/common/hooks :lem/common/var :lem/common/character) + (:local-nicknames (:line :lem/buffer/line)) (:use-reexport :lem/buffer/errors) (:use-reexport :lem/buffer/file-utils) (:use-reexport :lem/buffer/buffer-list-manager) @@ -81,6 +81,9 @@ ;; TODO: delete ugly exports :%buffer-clear-keep-binfo :%buffer-keep-binfo) + ;; undo.lisp + (:export + :with-inhibit-undo) (:export :buffer-list :any-modified-buffer-p @@ -188,6 +191,7 @@ :syntax-open-paren-char-p :syntax-closed-paren-char-p :syntax-string-quote-char-p + :syntax-equal-paren-p :syntax-escape-char-p :syntax-expr-prefix-char-p :syntax-skip-expr-prefix-forward @@ -264,9 +268,14 @@ :make-tm-patterns :make-tm-name :add-tm-repository - :add-tm-pattern)) + :add-tm-pattern) + ;; check-corruption.lisp + (:export + :corruption-warning + :check-all-buffers-corruption + :check-buffer-corruption)) -(defpackage :lem/buffer/indent +(uiop:define-package :lem/buffer/indent (:use :cl :lem/buffer/internal :lem/common/var) @@ -280,7 +289,7 @@ :indent-buffer :insert-string-and-indent)) -(defpackage :lem/buffer/encodings +(uiop:define-package :lem/buffer/encodings (:use :cl :lem/buffer/internal :lem/common/var) @@ -297,7 +306,7 @@ :encoding-read-detect-eol :encoding-check)) -(defpackage :lem/buffer/file +(uiop:define-package :lem/buffer/file (:use :cl :lem/buffer/internal :lem/buffer/encodings diff --git a/src/color-theme.lisp b/src/color-theme.lisp index 69fb32285..f7e498a0c 100644 --- a/src/color-theme.lisp +++ b/src/color-theme.lisp @@ -114,50 +114,16 @@ (when (current-theme) (get-color-theme-color (find-color-theme (current-theme)) name))) -(defun maybe-base-color (name) - (if (typep name 'base-color) - (base-color name) - name)) - -(define-major-mode color-theme-selector-mode () - (:name "Themes" - :keymap *color-theme-selector-keymap*)) - -(define-key *color-theme-selector-keymap* "Return" 'color-theme-selector-select) - -(define-command color-theme-selector-select () () - (with-point ((point (current-point))) - (line-start point) - (let ((theme (text-property-at point 'theme))) - (load-theme theme)))) - -(define-command list-color-themes () () - (let* ((buffer (make-buffer "*Color Themes*")) - (point (buffer-point buffer)) - (dark-themes '()) - (light-themes '())) - (with-buffer-read-only buffer nil - (erase-buffer buffer) - (dolist (name (all-color-themes)) - (let ((theme (find-color-theme name))) - (if (eq :dark (get-color-theme-color theme :display-background-mode)) - (push (cons name theme) dark-themes) - (push (cons name theme) light-themes)))) - (loop :for (name . theme) :in (append dark-themes light-themes) - :do (insert-string - point name - :attribute (make-attribute - :foreground (get-color-theme-color theme :foreground) - :background (get-color-theme-color theme :background)) - 'theme name) - (insert-character point #\newline))) - (buffer-start point) - (setf (buffer-read-only-p buffer) t) - (switch-to-buffer buffer) - (change-buffer-mode buffer 'color-theme-selector-mode))) - - +(defun ensure-color (color) + (typecase color + (base-color + (base-color color)) + (color + (color-to-hex-string color)) + (otherwise + color))) + (defun initialize-color-theme () - (load-theme (config :color-theme "decaf") nil)) + (load-theme (config :color-theme "lem-default") nil)) (add-hook *after-init-hook* 'initialize-color-theme) diff --git a/src/commands/buffer.lisp b/src/commands/buffer.lisp index d6eb396aa..bc0f77df6 100644 --- a/src/commands/buffer.lisp +++ b/src/commands/buffer.lisp @@ -4,7 +4,9 @@ :indent-current-buffer :toggle-read-only :rename-buffer - :unmark-buffer)) + :unmark-buffer) + #+sbcl + (:lock t)) (in-package :lem-core/commands/buffer) (defvar *read-only-function* nil) diff --git a/src/commands/edit.lisp b/src/commands/edit.lisp index 4498d2599..8b02e3598 100644 --- a/src/commands/edit.lisp +++ b/src/commands/edit.lisp @@ -32,7 +32,9 @@ :transpose-characters :undo :redo - :delete-trailing-whitespace)) + :delete-trailing-whitespace) + #+sbcl + (:lock t)) (in-package :lem-core/commands/edit) (setf (keymap-undef-hook *global-keymap*) 'self-insert) @@ -218,16 +220,20 @@ (kill-region start end)) (delete-previous-char)) +(defun yank-string (point string) + (change-yank-start point + (copy-point point :right-inserting)) + (insert-string-and-indent point string) + (change-yank-end point + (copy-point point :left-inserting)) + (continue-flag :yank)) + (defun yank-1 (arg) (let ((string (if (null arg) (yank-from-clipboard-or-killring) (peek-killring-item (current-killring) (1- arg))))) - (change-yank-start (current-point) - (copy-point (current-point) :right-inserting)) - (insert-string-and-indent (current-point) string) - (change-yank-end (current-point) - (copy-point (current-point) :left-inserting)) - (continue-flag :yank))) + (when string + (yank-string (current-point) string)))) (define-command yank (&optional arg) (:universal-nil) "Paste the copied text." @@ -449,3 +455,6 @@ (with-enable-clipboard (and (enable-clipboard-p) (null (buffer-fake-cursors (current-buffer)))) (process-each-cursors #'call-next-method))) + +(defmethod lem-core:paste-using-mode (mode text) + (yank-string (current-point) text)) diff --git a/src/commands/file.lisp b/src/commands/file.lisp index 3aa506320..3dfa8c2c6 100644 --- a/src/commands/file.lisp +++ b/src/commands/file.lisp @@ -20,13 +20,21 @@ :change-directory :current-directory :prompt-for-files-recursively - :format-current-buffer)) + :format-current-buffer + :file-history + :find-history-file + :*file-history-limit* + :get-file-mode + :format-current-buffer) + #+sbcl + (:lock t)) (in-package :lem-core/commands/file) (define-key *global-keymap* "C-x C-f" 'find-file) (define-key *global-keymap* "C-x C-r" 'read-file) (define-key *global-keymap* "C-x C-s" 'save-current-buffer) (define-key *global-keymap* "C-x C-w" 'write-file) +(define-key *global-keymap* "C-x C-h" 'find-history-file) (define-key *global-keymap* "C-x Tab" 'insert-file) (define-key *global-keymap* "C-x s" 'save-some-buffers) @@ -92,7 +100,7 @@ (setf buffer (execute-find-file *find-file-executor* (get-file-mode pathname) pathname))) - (when buffer + (when (bufferp buffer) (switch-to-buffer buffer t nil)))))) (defmethod execute-find-file :before (executor mode pathname) @@ -176,15 +184,15 @@ If finding files times out, such as in a HOME directory, stop the operation. Return a list of files or signal a FALLBACK-TO-FIND-FILE simple condition." - (let ((thread (bt:make-thread + (let ((thread (bt2:make-thread (lambda () (get-files-recursively find-program)) :name "Lem get-files-recursively"))) (handler-case - (bt:with-timeout (timeout) - (bt:join-thread thread)) - (bt:timeout () - (bt:destroy-thread thread) + (bt2:with-timeout (timeout) + (bt2:join-thread thread)) + (bt2:timeout () + (bt2:destroy-thread thread) (signal 'fallback-to-find-file))))) (defun prompt-for-files-recursively () @@ -204,7 +212,7 @@ (let ((candidates (get-files-recursively-with-timeout (find-program)))) (prompt-for-string "File: " - :completion-function (lambda (x) (completion-strings x candidates)) + :completion-function (lambda (x) (completion-files x candidates)) :test-function (lambda (name) (member name candidates :test #'string=)))))) (define-command find-file-recursively (arg) (:universal) @@ -389,3 +397,41 @@ With prefix argument INSERT, insert the directory of the active buffer at point. Supported modes include: c-mode with clang-format, go-mode with gofmt, js-mode and json-mode with prettier, and lisp-mode. Additionally rust-mode uses rustfmt." (format-buffer)) + +(defvar *files-history*) +(defvar *file-history-limit* 10 + "The maximum number of files to keep in the file history.") + +(defun file-history () + "Return or create the files' history struct. + The history file is saved on (lem-home)/history/files" + (unless (boundp '*files-history*) + (let* ((pathname (merge-pathnames "history/files" (lem-home))) + (history (lem/common/history:make-history :pathname pathname :limit *file-history-limit*))) + (setf *files-history* history))) + *files-history*) + +(defun add-to-file-history (buffer) + "Add the buffer's filename to the file history." + (let ((filename (buffer-filename buffer))) + (when filename + (lem/common/history:add-history (file-history) + (namestring filename) + :allow-duplicates nil + :move-to-top t) + (lem/common/history:save-file (file-history))))) + +(add-hook *find-file-hook* 'add-to-file-history) + +(define-command find-history-file () () + "Prompt for a file from the file history and open it." + (let* ((history (file-history)) + (candidates (lem/common/history:history-data-list history))) + (if candidates + (let ((filename (prompt-for-string + "File: " + :completion-function (lambda (x) (completion-strings x (reverse candidates))) + :test-function (lambda (name) (member name candidates :test #'string=))))) + (when filename + (find-file filename))) + (editor-error "No file history.")))) diff --git a/src/commands/font.lisp b/src/commands/font.lisp index 4749d0244..2582d8dfd 100644 --- a/src/commands/font.lisp +++ b/src/commands/font.lisp @@ -1,7 +1,9 @@ (defpackage :lem-core/commands/font (:use :cl :lem-core) (:export :font-size-increase - :font-size-decrease)) + :font-size-decrease) + #+sbcl + (:lock t)) (in-package :lem-core/commands/font) (define-key *global-keymap* "C-+" 'font-size-increase) @@ -17,4 +19,4 @@ (define-command font-size-set (size) ((:number "Size: ")) "Set the font size to an integer (this currently only works with SDL2 frontend)" - (lem-if:set-font-size (implementation) size)) \ No newline at end of file + (lem-if:set-font-size (implementation) size)) diff --git a/src/commands/frame.lisp b/src/commands/frame.lisp index 7091c76cc..5ff9927b7 100644 --- a/src/commands/frame.lisp +++ b/src/commands/frame.lisp @@ -1,6 +1,8 @@ (defpackage :lem-core/commands/frame (:use :cl :lem-core) - (:export :toggle-frame-fullscreen)) + (:export :toggle-frame-fullscreen) + #+sbcl + (:lock t)) (in-package :lem-core/commands/frame) (define-command toggle-frame-fullscreen () () diff --git a/src/commands/help.lisp b/src/commands/help.lisp index 3ab5903f0..b23df56b6 100644 --- a/src/commands/help.lisp +++ b/src/commands/help.lisp @@ -4,7 +4,9 @@ :describe-bindings :apropos-command :lem-version - :list-modes)) + :list-modes) + #+sbcl + (:lock t)) (in-package :lem-core/commands/help) (define-key *global-keymap* "C-x ?" 'describe-key) diff --git a/src/commands/mark.lisp b/src/commands/mark.lisp index 6b7f1b6a0..fe8b7dd76 100644 --- a/src/commands/mark.lisp +++ b/src/commands/mark.lisp @@ -2,7 +2,9 @@ (:use :cl :lem-core) (:export :mark-set :exchange-point-mark - :mark-set-whole-buffer)) + :mark-set-whole-buffer) + #+sbcl + (:lock t)) (in-package :lem-core/commands/mark) (define-key *global-keymap* "C-@" 'mark-set) diff --git a/src/commands/move.lisp b/src/commands/move.lisp index 508d3e315..35011dadd 100644 --- a/src/commands/move.lisp +++ b/src/commands/move.lisp @@ -16,7 +16,9 @@ :previous-page :next-page-char :previous-page-char - :goto-line)) + :goto-line) + #+sbcl + (:lock t)) (in-package :lem-core/commands/move) (define-key *global-keymap* "C-n" 'next-line) diff --git a/src/commands/multiple-cursors.lisp b/src/commands/multiple-cursors.lisp index 1ad8b8d71..078289e6e 100644 --- a/src/commands/multiple-cursors.lisp +++ b/src/commands/multiple-cursors.lisp @@ -1,6 +1,8 @@ (defpackage :lem-core/commands/multiple-cursors (:use :cl :lem-core) - (:export :add-cursors-to-next-line)) + (:export :add-cursors-to-next-line) + #+sbcl + (:lock t)) (in-package :lem-core/commands/multiple-cursors) (define-key *global-keymap* "M-C" 'add-cursors-to-next-line) diff --git a/src/commands/other.lisp b/src/commands/other.lisp index 0bb2b8322..f3bbd0f69 100644 --- a/src/commands/other.lisp +++ b/src/commands/other.lisp @@ -8,7 +8,9 @@ :quick-exit :execute-command :show-context-menu - :load-library)) + :load-library) + #+sbcl + (:lock t)) (in-package :lem-core/commands/other) (define-key *global-keymap* "NopKey" 'nop-command) diff --git a/src/commands/process.lisp b/src/commands/process.lisp index 8252ec44a..b002b5da8 100644 --- a/src/commands/process.lisp +++ b/src/commands/process.lisp @@ -1,7 +1,9 @@ (defpackage :lem-core/commands/process (:use :cl :lem-core) (:export :filter-buffer - :pipe-command)) + :pipe-command) + #+sbcl + (:lock t)) (in-package :lem-core/commands/process) (define-key *global-keymap* "C-x #" 'filter-buffer) diff --git a/src/commands/project.lisp b/src/commands/project.lisp index e5f894801..27744147b 100644 --- a/src/commands/project.lisp +++ b/src/commands/project.lisp @@ -6,10 +6,16 @@ :*delete-last-buffer* :root-p :find-root + :saved-projects :project-find-file :project-root-directory :project-root - :project-kill-buffers) + :project-kill-buffers + :project-switch + :project-save + :project-unsave) + #+sbcl + (:lock t) (:documentation "Defines utilities to find a project root directory and related user-facing commands: project-find-file, project-kill-buffers, project-switch etc.")) (in-package :lem-core/commands/project) diff --git a/src/commands/s-expression.lisp b/src/commands/s-expression.lisp index 8769abed7..c91b07396 100644 --- a/src/commands/s-expression.lisp +++ b/src/commands/s-expression.lisp @@ -10,7 +10,9 @@ :up-list :mark-sexp :kill-sexp - :transpose-sexps)) + :transpose-sexps) + #+sbcl + (:lock t)) (in-package :lem-core/commands/s-expression) (define-key *global-keymap* "C-M-f" 'forward-sexp) diff --git a/src/commands/window.lisp b/src/commands/window.lisp index 4edef48c5..88d864671 100644 --- a/src/commands/window.lisp +++ b/src/commands/window.lisp @@ -28,7 +28,9 @@ :read-file-next-window :select-buffer-next-window :switch-to-last-focused-window - :compare-windows)) + :compare-windows) + #+sbcl + (:lock t)) (in-package :lem-core/commands/window) (define-key *global-keymap* "C-x b" 'select-buffer) diff --git a/src/commands/word.lisp b/src/commands/word.lisp index 9d68fe381..50887590b 100644 --- a/src/commands/word.lisp +++ b/src/commands/word.lisp @@ -14,7 +14,9 @@ :forward-paragraph :backward-paragraph :kill-paragraph - :count-words)) + :count-words) + #+sbcl + (:lock t)) (in-package :lem-core/commands/word) (define-key *global-keymap* "M-f" 'forward-word) diff --git a/src/common/color.lisp b/src/common/color.lisp index eaa886aa4..e43a3fcee 100644 --- a/src/common/color.lisp +++ b/src/common/color.lisp @@ -5,6 +5,7 @@ :color-red :color-blue :color-green + :color-equal :light-color-p :parse-color :rgb-to-hsv @@ -770,25 +771,46 @@ 144 238 144 LightGreen ") -(let ((color-names - (flet ((parse (text) - (with-input-from-string (stream text) - (loop :for line := (read-line stream nil) - :while line - :for elt := (ppcre:register-groups-bind (r g b name) - ("^\\s*(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+([a-zA-Z0-9 ]+)" line) - (cons (string-downcase name) - (list (and r (parse-integer r)) - (and g (parse-integer g)) - (and b (parse-integer b))))) - :if elt - :collect elt)))) - (alexandria:alist-hash-table (parse *rgb.txt*) :test 'equal)))) - (defun get-rgb-from-color-name (color-name) - (gethash (string-downcase color-name) color-names))) +;; Size includes aliases +(defvar *color-names* (make-hash-table :size 848 :test 'equal)) + +(defun parse-rgb-txt () + (alexandria:alist-hash-table + (with-input-from-string (stream *rgb.txt*) + (loop :for line := (read-line stream nil) + :while line + :for elt := (ppcre:register-groups-bind (r g b name) + ("^\\s*(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+([a-zA-Z0-9 ]+)" line) + (cons (string-downcase name) + (list (and r (parse-integer r)) + (and g (parse-integer g)) + (and b (parse-integer b))))) + :if elt + :collect elt)) + :test 'equal)) + +;; Lisp-style color names with a dash instead of space +(defun add-lisp-color-alias (name value) + (when (> (length (ppcre:split "\\s+" name)) 1) + (let ((new-name (ppcre:regex-replace-all "\\s+" name "-"))) + (setf (gethash new-name *color-names*) value)))) + +(defun add-lisp-color-aliases () + (maphash #'add-lisp-color-alias *color-names*)) + +(defun get-rgb-from-color-name (color-name) + (when (equal (hash-table-count *color-names*) 0) + (setf *color-names* (parse-rgb-txt)) + (add-lisp-color-aliases)) + (gethash (string-downcase color-name) *color-names*)) (defstruct (color (:constructor make-color (red green blue))) red green blue) +(defun color-equal (color1 color2) + (and (= (color-red color1) (color-red color2)) + (= (color-green color1) (color-green color2)) + (= (color-blue color1) (color-blue color2)))) + (defun light-color-p (color) "Return t if COLOR is light." (let ((color (etypecase color diff --git a/src/common/history.lisp b/src/common/history.lisp index c6c2da03c..6afcf7d8a 100644 --- a/src/common/history.lisp +++ b/src/common/history.lisp @@ -20,7 +20,8 @@ pathname data index - edit-string) + edit-string + limit) (defun history-data-list (history) "Return the history data as a list (and not a vector)." @@ -31,7 +32,7 @@ (and (not (equal input last-input)) (not (equal input "")))) -(defun make-history (&key pathname) +(defun make-history (&key pathname limit) (let* ((initial-contents (when (and pathname (uiop:file-exists-p pathname)) (uiop:read-file-form pathname))) @@ -39,7 +40,8 @@ (%make-history :pathname pathname :data (make-array num-contents :fill-pointer num-contents :adjustable t :initial-contents initial-contents) - :index num-contents))) + :index num-contents + :limit limit))) (defun save-file (history) (when (history-pathname history) @@ -55,23 +57,45 @@ (aref (history-data history) (1- (length (history-data history)))))) -(defun add-history (history input &key (allow-duplicates t) (test #'equal)) - "Add this input to the history. - - Don't add the same input as the previous one. - If allow-duplicates is non t, don't add duplicates at all." - (cond - ((not allow-duplicates) - (when (not (find input (history-data history) :test test)) +(defun add-history (history input &key (allow-duplicates t) (test #'equal) move-to-top) + "Add this INPUT to the HISTORY. + + Doesn't add the same INPUT as the previous one. + If ALLOW-DUPLICATES is non T, don't add duplicates at all. + If LIMIT is set, overwrite oldest entry when reached. + If MOVE-TO-TOP is T, move the entry to the top of the stack if it already exists." + (let ((existing-position (position input (history-data history) :test test))) + (cond + ((and existing-position move-to-top) + (let ((item (aref (history-data history) existing-position))) + ;; Remove the item from its current position + (replace (history-data history) + (history-data history) + :start1 existing-position + :start2 (1+ existing-position)) + (decf (fill-pointer (history-data history))) + ;; Add the item to the top + (vector-push-extend item (history-data history)))) + ((and (not existing-position) + (require-additions-to-history-p input (last-history history))) + (let ((limit (history-limit history))) + (cond + ((and limit (>= (length (history-data history)) limit)) + ;; Shift by 1, overwriting the oldest + (replace (history-data history) + (history-data history) + :start1 0 + :start2 1 + :end2 limit) + (setf (aref (history-data history) (1- limit)) input)) + (t + ;; Add new element normally + (vector-push-extend input (history-data history)))))) + (allow-duplicates + ;; If duplicates are allowed, add the input (vector-push-extend input (history-data history)))) - ((require-additions-to-history-p input - (last-history history)) - (vector-push-extend input (history-data history))) - (t - nil)) - - (setf (history-index history) - (length (history-data history))) + (setf (history-index history) + (length (history-data history)))) input) (defun remove-history (history input) diff --git a/src/common/queue.lisp b/src/common/queue.lisp index 49367eff4..4b143a21b 100644 --- a/src/common/queue.lisp +++ b/src/common/queue.lisp @@ -39,30 +39,30 @@ (null (queue-list queue))) (defstruct (concurrent-queue (:constructor %make-concurrent-queue)) - (wait (bt:make-condition-variable)) - (lock (bt:make-lock)) + (wait (bt2:make-condition-variable)) + (lock (bt2:make-lock)) (queue (make-queue))) (defun make-concurrent-queue () (%make-concurrent-queue)) (defmethod len ((queue concurrent-queue)) - (bt:with-lock-held ((concurrent-queue-lock queue)) + (bt2:with-lock-held ((concurrent-queue-lock queue)) (len (concurrent-queue-queue queue)))) (defmethod enqueue ((queue concurrent-queue) obj) - (bt:with-lock-held ((concurrent-queue-lock queue)) + (bt2:with-lock-held ((concurrent-queue-lock queue)) (enqueue (concurrent-queue-queue queue) obj) - (bt:condition-notify (concurrent-queue-wait queue)))) + (bt2:condition-notify (concurrent-queue-wait queue)))) (defmethod dequeue ((queue concurrent-queue) &key timeout timeout-value) - (bt:with-lock-held ((concurrent-queue-lock queue)) + (bt2:with-lock-held ((concurrent-queue-lock queue)) (if (not (empty-p (concurrent-queue-queue queue))) (dequeue (concurrent-queue-queue queue)) (cond ((if timeout - (bt:condition-wait (concurrent-queue-wait queue) (concurrent-queue-lock queue) + (bt2:condition-wait (concurrent-queue-wait queue) (concurrent-queue-lock queue) :timeout timeout) - (bt:condition-wait (concurrent-queue-wait queue) (concurrent-queue-lock queue))) + (bt2:condition-wait (concurrent-queue-wait queue) (concurrent-queue-lock queue))) (dequeue (concurrent-queue-queue queue))) (t timeout-value))))) diff --git a/lib/socket-utils/socket.lisp b/src/common/socket.lisp similarity index 94% rename from lib/socket-utils/socket.lisp rename to src/common/socket.lisp index e4b59f00e..918192c87 100644 --- a/lib/socket-utils/socket.lisp +++ b/src/common/socket.lisp @@ -1,11 +1,11 @@ -(defpackage :lem-socket-utils +(defpackage :lem/common/socket (:use :cl) (:import-from :usocket) (:export :port-available-p :random-available-port) #+sbcl (:lock t)) -(in-package :lem-socket-utils) +(in-package :lem/common/socket) (defconstant +private-port-min+ 49152) (defconstant +private-port-max+ 65535) diff --git a/src/common/timer.lisp b/src/common/timer.lisp index ded1b4c06..0278ae7c7 100644 --- a/src/common/timer.lisp +++ b/src/common/timer.lisp @@ -112,24 +112,24 @@ (defclass timer () ((mutex :accessor timer-mutex - :type bt:lock) + :type bt2:lock) (stop-mailbox :accessor timer-stop-mailbox :type lem-mailbox:mailbox) (thread :accessor timer-thread - :type bt:thread))) + :type bt2:thread))) (defmethod timer-expired-p ((timer timer)) - (bt:with-lock-held ((timer-mutex timer)) + (bt2:with-lock-held ((timer-mutex timer)) (call-next-method))) (defmethod expire-timer ((timer timer)) - (bt:with-lock-held ((timer-mutex timer)) + (bt2:with-lock-held ((timer-mutex timer)) (set-timer-expired-p t timer))) (defmethod inspire-timer ((timer timer)) - (bt:with-lock-held ((timer-mutex timer)) + (bt2:with-lock-held ((timer-mutex timer)) (set-timer-expired-p nil timer))) (defun make-timer (function &key name handle-function) @@ -138,8 +138,8 @@ (defmethod start-timer ((timer timer) ms &key repeat) (setf (timer-ms timer) ms (timer-repeat-p timer) repeat - (timer-mutex timer) - (bt:make-lock "timer internal mutex")) + (timer-mutex timer) + (bt2:make-lock :name "timer internal mutex")) (start-timer-thread timer ms repeat) timer) @@ -153,7 +153,7 @@ (setf (timer-stop-mailbox timer) stop-mailbox) (setf (timer-thread timer) - (bt:make-thread + (bt2:make-thread (lambda () (loop (let ((recv-stop-msg diff --git a/src/completion.lisp b/src/completion.lisp index 91c5f98ed..1be0f0504 100644 --- a/src/completion.lisp +++ b/src/completion.lisp @@ -27,7 +27,9 @@ :when pos :collect (string (char string pos)) :while pos)))) -(defun completion (name elements &key (test #'search) separator key) +(defun completion (name elements &key (test #'search) separator key rank) + "Perform completion on ELEMENTS matching NAME. Returns matching elements, + optionally sorted by RANK function." (labels ((apply-key (elt) (if key (funcall key elt) elt)) (test-with-separator (elt) (let* ((elt (apply-key elt)) @@ -39,10 +41,53 @@ :always (funcall test p1 p2))))) (test-without-separator (elt) (funcall test name (apply-key elt)))) - (remove-if-not (if separator - #'test-with-separator - #'test-without-separator) - elements))) + (let ((filtered-elements + (remove-if-not (if separator + #'test-with-separator + #'test-without-separator) + elements))) + (if rank + (sort filtered-elements #'< :key (lambda (elt) (funcall rank name (apply-key elt)))) + filtered-elements)))) + +(defun string-completion-rank (name elt) + (cond + ; Exact match + ((string= name elt) 0) + ; Prefix match + ((str:starts-with-p name elt) (length name)) + ; Substring match anywhere + ((search name elt) 2) + ; Fuzzy match, rank by length + (t (length elt)))) + +(defun completion-strings (str strings &key key) + (completion str strings + :test #'fuzzy-match-p + :key key + :rank #'string-completion-rank)) + +(defun file-completion-rank (name elt) + (let ((file-name (file-namestring elt))) + (cond + ; Exact match + ((string= name elt) 0) + ; Prefix match in file name + ((str:starts-with-p name file-name) 1) + ; Substring match in file name + ((search name file-name) 2) + ; Prefix match in full path + ((str:starts-with-p name elt) 3) + ; Substring match in full path + ((search name elt) 4) + ; Fuzzy match, rank by length + (t (length elt))))) + +(defun completion-files (str strings &key key) + (completion str strings + :test #'fuzzy-match-p + :key key + :rank #'file-completion-rank)) (defun completion-hyphen (name elements &key key) (completion name elements :test #'completion-test :separator "-" :key key)) @@ -71,10 +116,7 @@ :key #'(lambda (path) (enough-namestring path input-directory)))))) strings))) - -(defun completion-strings (str strings &key key) - (completion str strings :test #'fuzzy-match-p :key key)) - + (defun completion-buffer (str &optional (buffer-list (buffer-list))) (let ((candidates1 (completion str buffer-list diff --git a/src/display/base.lisp b/src/display/base.lisp index 953aa3bfb..3e709e1e6 100644 --- a/src/display/base.lisp +++ b/src/display/base.lisp @@ -41,8 +41,9 @@ Used to prevent recursive `redraw-display` calls.") (window-redraw window force))) (redraw-current-window (current-window) force)) (redraw-header-windows (force) - (dolist (window (frame-header-windows (current-frame))) - (window-redraw window force))) + (let ((force (or force (not (null (frame-floating-windows (current-frame))))))) + (dolist (window (frame-header-windows (current-frame))) + (window-redraw window force)))) (redraw-floating-windows () (dolist (window (frame-floating-windows (current-frame))) (window-redraw window (redraw-after-modifying-floating-window (implementation))))) diff --git a/src/display/logical-line.lisp b/src/display/logical-line.lisp index fc5b65cd6..93ee0beba 100644 --- a/src/display/logical-line.lisp +++ b/src/display/logical-line.lisp @@ -151,7 +151,8 @@ string) (defstruct eol-cursor-item - attribute) + attribute + true-cursor-p) (defstruct extend-to-eol-item color) @@ -226,7 +227,8 @@ items)) (alexandria:when-let (attribute (logical-line-end-of-line-cursor-attribute logical-line)) - (push (make-eol-cursor-item :attribute attribute) + (push (make-eol-cursor-item :attribute attribute + :true-cursor-p (cursor-attribute-p attribute)) items)) (values (nreverse items) (alexandria:when-let (overlay diff --git a/src/display/physical-line.lisp b/src/display/physical-line.lisp index b2e0cdb7b..527adf916 100644 --- a/src/display/physical-line.lisp +++ b/src/display/physical-line.lisp @@ -34,7 +34,12 @@ (defclass eol-cursor-object (drawing-object) ((color :initarg :color - :reader eol-cursor-object-color))) + :reader eol-cursor-object-color) + (attribute :initarg :attribute + :reader eol-cursor-object-attribute) + (true-cursor-p :initarg :true-cursor-p + :initform nil + :reader eol-cursor-object-true-cursor-p))) (defclass extend-to-eol-object (drawing-object) ((color :initarg :color @@ -86,8 +91,9 @@ (extend-to-eol-object-color drawing-object-2))) (defmethod drawing-object-equal ((drawing-object-1 line-end-object) (drawing-object-2 line-end-object)) - (equal (line-end-object-offset drawing-object-1) - (line-end-object-offset drawing-object-2))) + (and (call-next-method) + (equal (line-end-object-offset drawing-object-1) + (line-end-object-offset drawing-object-2)))) (defmethod drawing-object-equal ((drawing-object-1 image-object) (drawing-object-2 image-object)) nil) @@ -137,9 +143,11 @@ (defun create-drawing-object (item) (cond ((and *line-wrap* (typep item 'eol-cursor-item)) (list (make-instance 'eol-cursor-object + :attribute (eol-cursor-item-attribute item) :color (parse-color (attribute-background - (eol-cursor-item-attribute item)))))) + (eol-cursor-item-attribute item))) + :true-cursor-p (eol-cursor-item-true-cursor-p item)))) ((typep item 'extend-to-eol-item) (list (make-instance 'extend-to-eol-object :color (extend-to-eol-item-color item)))) ((typep item 'line-end-item) diff --git a/src/event-queue.lisp b/src/event-queue.lisp index 1a42ffe11..e23a612c1 100644 --- a/src/event-queue.lisp +++ b/src/event-queue.lisp @@ -9,7 +9,7 @@ (enqueue *editor-event-queue* obj)) (defun send-abort-event (editor-thread force) - (bt:interrupt-thread editor-thread + (bt2:interrupt-thread editor-thread (lambda () (interrupt force)))) diff --git a/src/ext/context-menu.lisp b/src/ext/context-menu.lisp index 057f4ef00..8a03bd691 100644 --- a/src/ext/context-menu.lisp +++ b/src/ext/context-menu.lisp @@ -43,6 +43,7 @@ It is intended for use in dynamically computing menu items.")) (defmethod activate ((context-menu context-menu)) (when (context-menu-compute-items-function context-menu) + (setf (lem/multi-column-list::multi-column-list-search-string context-menu) "") (setf (lem/multi-column-list::multi-column-list-items context-menu) (funcall (context-menu-compute-items-function context-menu))))) diff --git a/src/ext/directory-mode.lisp b/src/ext/directory-mode.lisp index 840cbbb77..4d6efc433 100644 --- a/src/ext/directory-mode.lisp +++ b/src/ext/directory-mode.lisp @@ -395,10 +395,19 @@ (update (current-buffer))) (define-command directory-mode-up-directory () () - (switch-to-buffer - (directory-buffer - (uiop:pathname-parent-directory-pathname - (buffer-directory))))) + (let ((dir (buffer-directory))) + (switch-to-buffer + (directory-buffer (uiop:pathname-parent-directory-pathname (buffer-directory)))) + (search-filename-and-recenter + (concatenate + 'string + (car + (reverse + (split-sequence:split-sequence + (uiop:directory-separator-for-host) + dir + :remove-empty-subseqs t))) + (string (uiop:directory-separator-for-host)))))) (define-command directory-mode-find-file () () (process-current-line-pathname 'find-file)) @@ -653,6 +662,8 @@ With prefix argument ARG, unmark all those files." "Open this file's directory and place point on the filename." (let ((fullpath (buffer-filename))) (cond + ((mode-active-p (current-buffer) 'directory-mode) + (directory-mode-up-directory)) ((null fullpath) (message "No file at point")) (t @@ -678,3 +689,5 @@ This does not delete the marked entries, but only remove them from the buffer." (defmethod execute :after ((mode directory-mode) command argument) (when (mode-active-p (current-buffer) 'directory-mode) (update-line (current-point)))) + + diff --git a/src/ext/frame-multiplexer.lisp b/src/ext/frame-multiplexer.lisp index 1026f0a45..5ff5e683b 100644 --- a/src/ext/frame-multiplexer.lisp +++ b/src/ext/frame-multiplexer.lisp @@ -4,13 +4,27 @@ :lem/button :lem/common/ring) (:export :*keymap* + :frame-multiplexer-active-frame-name-attribute + :frame-multiplexer-frame-name-attribute + :frame-multiplexer-background-attribute + :frame-multiplexer-advice :frame-multiplexer-next :frame-multiplexer-prev :frame-multiplexer-switch + :frame-multiplexer-switch-0 + :frame-multiplexer-switch-1 + :frame-multiplexer-switch-2 + :frame-multiplexer-switch-3 + :frame-multiplexer-switch-4 + :frame-multiplexer-switch-5 + :frame-multiplexer-switch-6 + :frame-multiplexer-switch-7 + :frame-multiplexer-switch-8 + :frame-multiplexer-switch-9 :frame-multiplexer-create-with-new-buffer-list :frame-multiplexer-delete :frame-multiplexer-recent - :frame-mulitplexer-rename + :frame-multiplexer-rename :toggle-frame-multiplexer :frame-multiplexer-normalize-ids) #+sbcl @@ -43,10 +57,11 @@ "Keymap for commands related to the frame-multiplexer.") (define-key *keymap* "c" 'frame-multiplexer-create-with-new-buffer-list) +(define-key *keymap* "C" 'frame-multiplexer-create-with-previous-buffer) (define-key *keymap* "d" 'frame-multiplexer-delete) (define-key *keymap* "p" 'frame-multiplexer-prev) (define-key *keymap* "n" 'frame-multiplexer-next) -(define-key *keymap* "r" 'frame-mulitplexer-rename) +(define-key *keymap* "r" 'frame-multiplexer-rename) (define-key *global-keymap* "C-z" *keymap*) (defstruct tab @@ -311,11 +326,15 @@ *virtual-frame-map*) (clrhash *virtual-frame-map*))) -(define-command toggle-frame-multiplexer () () +(defclass frame-multiplexer-advice () ()) + +(define-command (toggle-frame-multiplexer (:advice-classes frame-multiplexer-advice)) () () (setf (variable-value 'frame-multiplexer :global) (not (variable-value 'frame-multiplexer :global)))) -(define-command frame-multiplexer-normalize-ids () () +(define-command (frame-multiplexer-normalize-ids (:advice-classes frame-multiplexer-advice)) + () + () "Normalize the IDs of all the frames. Assigns a smaller ID to a frame, if there is a smaller unused ID. This does not change the order of the frames." @@ -334,7 +353,10 @@ This does not change the order of the frames." (setf (aref (virtual-frame-id/frame-table vf) index) nil) (setq free-index (next-free (1+ free-index))))))) -(define-command frame-multiplexer-create-with-new-buffer-list () () +(define-command (frame-multiplexer-create-with-new-buffer-list + (:advice-classes frame-multiplexer-advice)) + () + () (check-frame-multiplexer-usable) (let* ((vf (gethash (implementation) *virtual-frame-map*)) (id (find-unused-frame-id vf))) @@ -344,7 +366,25 @@ This does not change the order of the frames." (allocate-frame vf frame) (switch-current-frame vf frame)))) -(define-command frame-multiplexer-delete (&optional id) (:universal-nil) +(define-command (frame-multiplexer-create-with-previous-buffer + (:advice-classes frame-multiplexer-advice)) + () + () + "Create a new frame with the previously opened buffer." + (check-frame-multiplexer-usable) + (let* ((vf (gethash (implementation) *virtual-frame-map*)) + (id (find-unused-frame-id vf))) + (when (null id) + (editor-error "It's full of frames in virtual frame")) + (let* ((prev-buffer (current-buffer)) + (new-frame (make-frame (current-frame)))) + (allocate-frame vf new-frame) + (switch-current-frame vf new-frame) + (when prev-buffer + (switch-to-buffer prev-buffer))))) + +(define-command (frame-multiplexer-delete (:advice-classes frame-multiplexer-advice)) + (&optional id) (:universal-nil) "Delete the current frame. With prefix argument ID, delete the frame with the given ID." (check-frame-multiplexer-usable) @@ -361,7 +401,8 @@ With prefix argument ID, delete the frame with the given ID." (switch-current-frame vf (search-previous-frame vf frame-now))) (free-frame vf frame-now)))) -(define-command frame-multiplexer-prev (&optional (n 1)) (:universal) +(define-command (frame-multiplexer-prev (:advice-classes frame-multiplexer-advice)) + (&optional (n 1)) (:universal) "Switch to the Nth previous frame. The prefix argument N defaults to 1." (check-frame-multiplexer-usable) @@ -373,7 +414,8 @@ The prefix argument N defaults to 1." (when frame (switch-current-frame vf frame)))) -(define-command frame-multiplexer-next (&optional (n 1)) (:universal) +(define-command (frame-multiplexer-next (:advice-classes frame-multiplexer-advice)) + (&optional (n 1)) (:universal) "Switch to the Nth next frame. The prefix argument N defaults to 1." (check-frame-multiplexer-usable) @@ -385,7 +427,8 @@ The prefix argument N defaults to 1." (when frame (switch-current-frame vf frame)))) -(define-command frame-multiplexer-switch (&optional (id 1)) (:universal) +(define-command (frame-multiplexer-switch (:advice-classes frame-multiplexer-advice)) + (&optional (id 1)) (:universal) "Switch to the frame with ID. The prefix argument ID defaults to 1." ;; TODO: It would be great to enhance this by showing a prompt @@ -397,7 +440,23 @@ The prefix argument ID defaults to 1." (switch-current-frame vf (frame-table-entry-frame entry)) (editor-error "No frame with ID ~a" id)))) -(define-command frame-multiplexer-recent (&optional (n 1)) (:universal) +(macrolet ((def (command-name n) + `(define-command (,command-name (:advice-classes frame-multiplexer-advice)) + () () + (frame-multiplexer-switch ,n)))) + (def frame-multiplexer-switch-0 0) + (def frame-multiplexer-switch-1 1) + (def frame-multiplexer-switch-2 2) + (def frame-multiplexer-switch-3 3) + (def frame-multiplexer-switch-4 4) + (def frame-multiplexer-switch-5 5) + (def frame-multiplexer-switch-6 6) + (def frame-multiplexer-switch-7 7) + (def frame-multiplexer-switch-8 8) + (def frame-multiplexer-switch-9 9)) + +(define-command (frame-multiplexer-recent (:advice-classes frame-multiplexer-advice)) + (&optional (n 1)) (:universal) "Switch to the Nth most recent frame selected. The prefix argument N defaults to 1." (check-frame-multiplexer-usable) @@ -418,7 +477,8 @@ The prefix argument N defaults to 1." (let ((entry (aref (virtual-frame-id/frame-table vf) recent-frame-id))) (switch-current-frame vf (frame-table-entry-frame entry))))))) -(define-command frame-mulitplexer-rename (name &optional id) ((:string "New name: ") :universal-nil) +(define-command (frame-multiplexer-rename (:advice-classes frame-multiplexer-advice)) + (name &optional id) ((:string "New name: ") :universal-nil) "Rename the current frame to NAME. With prefix argument ID, rename the frame with the given ID." (check-frame-multiplexer-usable) diff --git a/src/ext/grep.lisp b/src/ext/grep.lisp index 1707aab51..536db7035 100644 --- a/src/ext/grep.lisp +++ b/src/ext/grep.lisp @@ -1,7 +1,10 @@ (defpackage :lem/grep (:use :cl :lem) - (:export :grep) + (:export + :grep + :*grep-command* + :*grep-args*) #+sbcl (:lock t)) (in-package :lem/grep) @@ -56,18 +59,21 @@ (declare (ignore end old-len)) (let ((string (get-content-string start)) (move (lem/peek-source:get-move-function start))) - (with-point ((point (funcall move))) - (with-point ((start point) - (end point)) - (line-start start) - (line-end end) - (buffer-undo-boundary (point-buffer start)) - (delete-between-points start end) - (insert-string start string) - (buffer-undo-boundary (point-buffer start))))) + (when move + (with-point ((point (funcall move))) + (with-point ((start point) + (end point)) + (line-start start) + (line-end end) + (buffer-undo-boundary (point-buffer start)) + (delete-between-points start end) + (insert-string start string) + (buffer-undo-boundary (point-buffer start)))))) (lem/peek-source:show-matched-line)) -(defvar *last-query* "git grep -nH ") +(defvar *grep-command* "git grep") +(defvar *grep-args* "-nHI") +(defvar *last-query* (str:concat *grep-command* " " *grep-args* " ")) (defvar *last-directory* nil) (define-command grep (query &optional (directory (buffer-directory))) @@ -85,7 +91,9 @@ (loop :for (file line-number content) :in result :do (lem/peek-source:with-appending-source (point :move-function (make-move-function directory file line-number)) - (insert-string point file :attribute 'lem/peek-source:filename-attribute :read-only t) + (insert-string point file :attribute 'lem/peek-source:filename-attribute + :mode 'peek-grep-mode + :read-only t) (insert-string point ":" :read-only t) (insert-string point (princ-to-string line-number) :attribute 'lem/peek-source:position-attribute @@ -97,6 +105,26 @@ (setf *last-query* query *last-directory* directory))) +(define-command project-grep () () + "Run grep at the project root directory." + (let* ((cwd (buffer-directory)) + (project-root (lem-core/commands/project:find-root cwd)) + (root (or project-root cwd)) + (query (prompt-for-string "" :initial-value *last-query* :history-symbol 'grep))) + (grep query root))) + +(define-command grep-move-to-content-start () () + "Move to the first non-whitespace content character in the current line." + (with-point ((p (current-point))) + (line-start p) + (loop while (and (not (end-line-p p)) + (not (text-property-at p :content-start))) + do (character-offset p 1)) + (move-point (current-point) p) + ;; Skip trailing ":" and whitespace + (forward-char) + (skip-whitespace-forward (current-point) t))) + (define-command grep-help () () "Show grep help." (with-pop-up-typeout-window (s (make-buffer "*Help*") :erase t) @@ -105,7 +133,8 @@ (format s "The left window shows grep results, the right window shows a result in its source file.~&") (format s "~%") (format s "Available keybindings:~&") - (format s "- up/down arrows or C-p/C-n: go to the previous/next line~&") + (format s "- up/down arrows, n/p, or C-p/C-n: go to the previous/next line~&") + (format s "- a: move to the content in the current line~&") (format s "- C-x o or M-o: go to the other window~&") (format s "- Enter: visit the file of the result at point~&") (format s "- Escape or C-x 0: quit~&") @@ -115,5 +144,14 @@ (format s "You can use editing tools such as M-x query-replace in the results buffer.~&") (format s "~%"))) -;; TODO: Prepare keymap for grep-mode -(define-key lem/peek-source::*peek-source-keymap* "C-x ?" 'grep-help) ;; originally bound to describe-key. +(defvar *peek-grep-mode-keymap* (make-keymap :name '*peek-grep-mode-keymap* + :parent lem/peek-source:*peek-source-keymap*)) +(define-minor-mode peek-grep-mode + (:name "Peek" + :keymap *peek-grep-mode-keymap*)) + +(define-key *global-keymap* "C-x p g" 'project-grep) +(define-key *peek-grep-mode-keymap* "C-x ?" 'grep-help) ;; originally bound to describe-key. +(define-key *peek-grep-mode-keymap* "n" 'lem/peek-source:peek-source-next) +(define-key *peek-grep-mode-keymap* "p" 'lem/peek-source:peek-source-previous) +(define-key *peek-grep-mode-keymap* "a" 'grep-move-to-content-start) diff --git a/src/ext/line-numbers.lisp b/src/ext/line-numbers.lisp index d2ebf6744..ed011fc9b 100644 --- a/src/ext/line-numbers.lisp +++ b/src/ext/line-numbers.lisp @@ -2,6 +2,7 @@ (:use :cl :lem) (:export :*relative-line* :line-numbers-attribute + :active-line-number-attribute :line-numbers :toggle-line-numbers) #+sbcl @@ -17,7 +18,10 @@ (defvar *line-number-format* nil) (define-attribute line-numbers-attribute - (t :foreground :base07 :background :base01)) + (t :foreground :base07 :background :base01)) + +(define-attribute active-line-number-attribute + (t :foreground :base07 :background :base01)) (define-editor-variable line-numbers nil "" (lambda (value) @@ -51,6 +55,10 @@ With a positive universal argument, use relative line numbers. Also obey the glo (defmethod lem-core:compute-left-display-area-content ((mode line-numbers-mode) buffer point) (when (buffer-filename (point-buffer point)) - (let* ((string (format nil "~6D " (compute-line buffer point)))) + (let* ((string (format nil "~6D " (compute-line buffer point))) + (attribute (if (eq (compute-line buffer point) + (compute-line buffer (buffer-point buffer))) + `((0 ,(length string) active-line-number-attribute)) + `((0 ,(length string) line-numbers-attribute))))) (lem/buffer/line:make-content :string string - :attributes `((0 ,(length string) line-numbers-attribute)))))) + :attributes attribute)))) diff --git a/src/ext/listener-mode.lisp b/src/ext/listener-mode.lisp index 514c9e21d..0003e2f60 100644 --- a/src/ext/listener-mode.lisp +++ b/src/ext/listener-mode.lisp @@ -21,6 +21,8 @@ :listener-return :listener-previous-input :listener-next-input + :listener-previous-startswith-input + :listener-next-startswith-input :listener-previous-matching-input :listener-clear-buffer :listener-clear-input) @@ -168,6 +170,47 @@ (when win (replace-textarea buffer str)))) +(define-command listener-previous-startswith-input () () + (block nil + (let* ((buffer (current-buffer)) + (point (buffer-point buffer)) + (charpos (point-charpos point)) + (prefix (points-to-string (input-start-point buffer) point))) + (backup-edit-string (current-buffer)) + (flet ((commit (str) + (replace-textarea buffer str) + (setf (point-charpos point) charpos) + (return))) + (loop + (multiple-value-bind (str win) + (lem/common/history:previous-history (current-listener-history)) + (if win + (when (eql 0 (search prefix str :test #'string=)) + (commit str)) + (return)))))))) + +(define-command listener-next-startswith-input () () + (block nil + (let* ((buffer (current-buffer)) + (point (buffer-point buffer)) + (charpos (point-charpos point)) + (prefix (points-to-string (input-start-point buffer) point))) + (backup-edit-string (current-buffer)) + (flet ((commit (str) + (replace-textarea buffer str) + (setf (point-charpos point) charpos) + (return)) + (rollback () + (restore-edit-string buffer) + (return))) + (loop + (multiple-value-bind (str win) + (lem/common/history:next-history (current-listener-history)) + (if win + (when (eql 0 (search prefix str :test #'string=)) + (commit str)) + (rollback)))))))) + (define-command listener-previous-input () () (backup-edit-string (current-buffer)) (multiple-value-bind (str win) diff --git a/src/ext/peek-source.lisp b/src/ext/peek-source.lisp index a84ffc631..02f4249b5 100644 --- a/src/ext/peek-source.lisp +++ b/src/ext/peek-source.lisp @@ -8,7 +8,10 @@ :collector-buffer :get-move-function :show-matched-line - :highlight-matched-line) + :highlight-matched-line + :peek-source-next + :peek-source-previous + :*peek-source-keymap*) #+sbcl (:lock t)) (in-package :lem/peek-source) diff --git a/src/ext/popup-window.lisp b/src/ext/popup-window.lisp index 2ec2a8034..6789c055b 100644 --- a/src/ext/popup-window.lisp +++ b/src/ext/popup-window.lisp @@ -353,8 +353,10 @@ :width width :height height :border-size border-size) + ;; XXX: workaround for context-menu + (unless (typep gravity 'gravity-cursor) + (lem-core::window-set-pos destination-window + (+ x border-size) + (+ y border-size))) (lem-core::window-set-size destination-window w h) - (lem-core::window-set-pos destination-window - (+ x border-size) - (+ y border-size)) destination-window))) diff --git a/src/ext/showparen.lisp b/src/ext/showparen.lisp index 90cc168ba..444b957c8 100644 --- a/src/ext/showparen.lisp +++ b/src/ext/showparen.lisp @@ -2,7 +2,8 @@ (:use :cl :alexandria :lem) - (:export :showparen-attribute + (:export :enable + :showparen-attribute :forward-matching-paren :backward-matching-paren) #+sbcl @@ -16,6 +17,7 @@ (define-editor-variable forward-matching-paren 'forward-matching-paren-default) (define-editor-variable backward-matching-paren 'backward-matching-paren-default) +(define-editor-variable enable t) (defun forward-matching-paren-default (window point) (when (syntax-open-paren-char-p (character-at point)) @@ -51,15 +53,16 @@ (show-paren-at-point window point 0)))))) (defun update-show-paren () - (mapc #'delete-overlay *brackets-overlays*) - (setq *brackets-overlays* nil) - (let ((highlight-points (show-paren-at-point (current-window) (current-point)))) - (nconcf highlight-points (mouse-hover-highlight)) - (dolist (point highlight-points) - (push (make-overlay point - (character-offset (copy-point point :temporary) 1) - 'showparen-attribute) - *brackets-overlays*)))) + (when (variable-value 'enable) + (mapc #'delete-overlay *brackets-overlays*) + (setq *brackets-overlays* nil) + (let ((highlight-points (show-paren-at-point (current-window) (current-point)))) + (nconcf highlight-points (mouse-hover-highlight)) + (dolist (point highlight-points) + (push (make-overlay point + (character-offset (copy-point point :temporary) 1) + 'showparen-attribute) + *brackets-overlays*))))) (defvar *show-paren-timer* nil) diff --git a/src/ext/tabbar.lisp b/src/ext/tabbar.lisp index f7f19ee9a..e6e3833cf 100644 --- a/src/ext/tabbar.lisp +++ b/src/ext/tabbar.lisp @@ -35,6 +35,13 @@ (defun tabbar-init () (let ((buffer (make-buffer "*tabbar*" :enable-undo-p nil :temporary t))) + (when (display-light-p) + (set-attribute-foreground 'tabbar-active-tab-attribute (foreground-color)) + (set-attribute-foreground 'tabbar-attribute (foreground-color))) + (when (display-dark-p) + (set-attribute-foreground 'tabbar-active-tab-attribute (foreground-color)) + (set-attribute-background 'tabbar-active-tab-attribute "light gray") + (set-attribute-foreground 'tabbar-attribute (foreground-color))) (setf (variable-value 'line-wrap :buffer buffer) nil) (setf *tabbar* (make-instance 'tabbar-window :buffer buffer)))) diff --git a/src/ext/themes.lisp b/src/ext/themes.lisp index afd4ff546..9145040f2 100644 --- a/src/ext/themes.lisp +++ b/src/ext/themes.lisp @@ -33,3 +33,62 @@ (syntax-function-name-attribute :foreground "LightSkyBlue") (syntax-variable-attribute :foreground "LightGoldenrod") (syntax-type-attribute :foreground "PaleGreen")) + +;; decaf base +(lem-core:define-color-theme "lem-default" + nil + (:display-background-mode :dark) + (:foreground "#cccccc") + (:background "#2d2d2d") + (:inactive-window-background "#2d2d2d") + (lem-core:region :foreground nil :background "#515151") + (lem-core:syntax-warning-attribute :foreground "#ff7f7b") + (lem/buffer/internal:syntax-string-attribute :foreground "#beda78") + (lem/buffer/internal:syntax-comment-attribute :foreground "#777777") + (lem/buffer/internal:syntax-keyword-attribute :foreground "#efb3f7") + (lem/buffer/internal:syntax-constant-attribute :foreground "#ffbf70") + (lem/buffer/internal:syntax-function-name-attribute :foreground "#90bee1") + (lem/buffer/internal:syntax-variable-attribute :foreground "#ff7f7b") + (lem/buffer/internal:syntax-type-attribute :foreground "#ffd67c") + (lem-core:syntax-builtin-attribute :foreground "#bed6ff") + (:base00 "#2d2d2d") + (:base01 "#393939") + (:base02 "#515151") + (:base03 "#777777") + (:base04 "#b4b7b4") + (:base05 "#cccccc") + (:base06 "#e0e0e0") + (:base07 "#ffffff") + (:base08 "#ff7f7b") + (:base09 "#ffbf70") + (:base0a "#ffd67c") + (:base0b "#beda78") + (:base0c "#bed6ff") + (:base0d "#90bee1") + (:base0e "#efb3f7") + (:base0f "#ff93b3") + + (lem/frame-multiplexer:frame-multiplexer-active-frame-name-attribute + :foreground "white" :background "CornflowerBlue" :bold t) + (lem/frame-multiplexer:frame-multiplexer-frame-name-attribute + :foreground "black" :background "dark gray" :bold t) + (lem/frame-multiplexer:frame-multiplexer-background-attribute + :foreground "white" :background "#262626") + + (document-header1-attribute :foreground "#ffffff" :bold t) + (document-header2-attribute :foreground "#90bee1" :bold t) + (document-header3-attribute :foreground "#bed6ff" :bold t) + (document-header4-attribute :foreground "#efb3f7" :bold t) + (document-header5-attribute :foreground "#ffbf70" :bold t) + (document-header6-attribute :foreground "#beda78" :bold t) + (document-bold-attribute :bold t) + (document-italic-attribute :foreground "#ffbf70") + (document-underline-attribute :underline t) + (document-link-attribute :foreground "#90bee1" :underline t) + (document-list-attribute :foreground "#ffbf70") + (document-code-block-attribute :background "#393939" :foreground "#e0e0e0") + (document-inline-code-attribute :background "#393939" :foreground "#ff93b3") + (document-blockquote-attribute :foreground "#b4b7b4") + (document-table-attribute :foreground "#ffffff" :background "#515151") + (document-task-list-attribute :foreground "#beda78") + (document-metadata-attribute :foreground "#90bee1")) diff --git a/src/highlight-line.lisp b/src/highlight-line.lisp index a76904e05..ed4dc4fdd 100644 --- a/src/highlight-line.lisp +++ b/src/highlight-line.lisp @@ -13,4 +13,4 @@ (hsv-to-rgb h s (max 0 (- v 2))) - (format nil "#~2,'0X~2,'0X~2,'0X" r g b)))))) + (color-to-hex-string (make-color r g b))))))) diff --git a/src/html-buffer.lisp b/src/html-buffer.lisp new file mode 100644 index 000000000..36edde6a6 --- /dev/null +++ b/src/html-buffer.lisp @@ -0,0 +1,25 @@ +(in-package :lem-core) + +(defclass html-buffer (text-buffer) + ((html :initarg :html + :reader html-buffer-html) + (updated :initform nil + :reader html-buffer-updated-p))) + +(defmethod (setf html-buffer-html) (html (buffer html-buffer)) + (setf (slot-value buffer 'updated) t) + (setf (slot-value buffer 'html) html)) + +(defmethod invalidate-html-buffer-updated ((buffer html-buffer)) + (setf (slot-value buffer 'updated) nil)) + +(defun make-html-buffer (buffer-name html) + (let ((buffer (make-buffer buffer-name))) + (change-class buffer 'html-buffer :html html) + buffer)) + +(defun js-eval (window code &key (wait nil)) + (lem-if:js-eval (implementation) + (window-view window) + code + :wait wait)) diff --git a/src/input.lisp b/src/input.lisp index 7d4939aea..7b88470fb 100644 --- a/src/input.lisp +++ b/src/input.lisp @@ -107,7 +107,7 @@ (defun sit-for (seconds &optional (update-window-p t) (force-update-p nil)) (when update-window-p (redraw-display :force force-update-p)) (let ((e (receive-event seconds))) - (cond ((null e) t) + (cond ((null e) :timeout) ((abort-key-p e) (error 'editor-abort)) - ((key-p e) (unread-key e)) + ((key-p e) (unread-key e) e) (t nil)))) diff --git a/src/interface.lisp b/src/interface.lisp index d9ea2bbeb..2f8d3acc7 100644 --- a/src/interface.lisp +++ b/src/interface.lisp @@ -38,6 +38,9 @@ (defvar lem-if:*background-color-of-drawing-window* nil) +(deftype cursor-type () + '(member :box :bar :underline)) + (defgeneric lem-if:invoke (implementation function)) (defgeneric lem-if:get-background-color (implementation)) (defgeneric lem-if:get-foreground-color (implementation)) @@ -114,6 +117,11 @@ (defgeneric lem-if:object-height (implementation drawing-object)) (defgeneric lem-if:clear-to-end-of-window (implementation view y)) +(defgeneric lem-if:js-eval (implementation view code &key wait) + (:method (implementation view code &key wait) + (declare (ignore wait)) + (error "unimplemented"))) + (defvar *display-background-mode* nil) (defun implementation () @@ -121,10 +129,10 @@ (defmacro with-implementation (implementation &body body) `(let* ((*implementation* ,implementation) - (bt:*default-special-bindings* + (bt2:*default-special-bindings* (acons '*implementation* *implementation* - bt:*default-special-bindings*))) + bt2:*default-special-bindings*))) ,@body)) (defun display-background-mode () diff --git a/src/internal-packages.lisp b/src/internal-packages.lisp index 9e7bcca28..93b3eab95 100644 --- a/src/internal-packages.lisp +++ b/src/internal-packages.lisp @@ -7,6 +7,8 @@ :emoji-object :eol-cursor-object :eol-cursor-object-color + :eol-cursor-object-attribute + :eol-cursor-object-true-cursor-p :extend-to-eol-object :extend-to-eol-object-color :folder-object @@ -144,6 +146,23 @@ :syntax-variable-attribute :syntax-type-attribute :syntax-builtin-attribute + :document-header1-attribute + :document-header2-attribute + :document-header3-attribute + :document-header4-attribute + :document-header5-attribute + :document-header6-attribute + :document-bold-attribute + :document-italic-attribute + :document-underline-attribute + :document-link-attribute + :document-list-attribute + :document-code-block-attribute + :document-inline-code-attribute + :document-blockquote-attribute + :document-table-attribute + :document-task-list-attribute + :document-metadata-attribute :completion-attribute :non-focus-completion-attribute :attribute-image @@ -151,7 +170,9 @@ :attribute-height :attribute-font :cursor-attribute-p - :set-cursor-attribute) + :set-cursor-attribute + :display-dark-p + :display-light-p) ;; clipboard.lisp (:export :wsl-p @@ -252,6 +273,8 @@ :*window-scroll-functions* :*window-size-change-functions* :*window-show-buffer-functions* + :*switch-to-buffer-hook* + :*switch-to-window-hook* :window-parent :scroll :window-view-point @@ -365,6 +388,17 @@ :modeline-mode-names :modeline-position :modeline-posline + :modeline-name-attribute + :modeline-major-mode-attribute + :inactive-modeline-major-mode-attribute + :modeline-minor-modes-attribute + :inactive-modeline-minor-modes-attribute + :modeline-position-attribute + :inactive-modeline-position-attribute + :modeline-posline-attribute + :inactive-modeline-position-attribute + :inactive-modeline-name-attribute + :inactive-modeline-posline-attribute :convert-modeline-element) ;; command.lisp (:export @@ -408,7 +442,8 @@ :clear-region-major-mode :major-mode-at-point :current-major-mode-at-point - :with-major-mode) + :with-major-mode + :paste-using-mode) ;; keymap.lisp (:export :*keymaps* @@ -507,6 +542,7 @@ :completion-hyphen :completion-file :completion-strings + :completion-files :completion-buffer) ;; cursors.lisp (:export @@ -574,7 +610,9 @@ :attribute-foreground-color :attribute-background-color :attribute-foreground-with-reverse - :attribute-background-with-reverse) + :attribute-background-with-reverse + :cursor-type + :display-background-mode) ;; color-theme.lisp (:export :color-theme-names @@ -583,7 +621,9 @@ :current-theme :find-color-theme :color-theme - :get-color-theme-color) + :get-color-theme-color + :foreground-color + :background-color) ;; region.lisp (:export :check-marked-using-global-mode @@ -596,7 +636,19 @@ :*auto-format* :register-formatter :register-formatters - :format-buffer)) + :format-buffer) + ;; html-buffer.lisp + (:export + :html-buffer + :html-buffer-html + :make-html-buffer + :html-buffer-updated-p + :invalidate-html-buffer-updated + :js-eval) + ;; site-init.lisp + (:export + :*inits-directory-name* + :load-site-init)) #+sbcl (sb-ext:lock-package :lem-core) @@ -661,6 +713,7 @@ :get-char-width :get-char-height :clear-to-end-of-window + :js-eval :render-line :render-line-on-modeline :object-width diff --git a/src/interp.lisp b/src/interp.lisp index 34c33fbbd..5fcd7d4b7 100644 --- a/src/interp.lisp +++ b/src/interp.lisp @@ -145,7 +145,7 @@ (signal 'exit-editor :report report)) (defun call-background-job (function cont) - (bt:make-thread + (bt2:make-thread (lambda () (let ((error-text)) (handler-case diff --git a/src/key.lisp b/src/key.lisp index b9f9fabb7..a68e512c7 100644 --- a/src/key.lisp +++ b/src/key.lisp @@ -1,7 +1,7 @@ (in-package :lem-core) (defvar *named-key-syms* - '("Backspace" "Delete" "Down" "End" "Escape" "F0" "F1" "F10" "F11" "F12" "F2" "F3" "F4" "F5" "F6" "F7" "F8" "F9" + '("Backspace" "Insert" "Delete" "Down" "End" "Escape" "F0" "F1" "F10" "F11" "F12" "F2" "F3" "F4" "F5" "F6" "F7" "F8" "F9" "Home" "Left" "NopKey" "PageDown" "PageUp" "Return" "Right" "Space" "Tab" "Up")) (defun named-key-sym-p (key-sym) @@ -34,7 +34,9 @@ (when meta (write-string "M-" stream)) (when ctrl (write-string "C-" stream)) (when shift (write-string "Shift-" stream)) - (write-string sym stream))) + (if (string= sym " ") + (write-string "Space" stream) + (write-string sym stream)))) (defvar *key-constructor-cache* (make-hash-table :test 'equal)) diff --git a/src/keymap.lisp b/src/keymap.lisp index 542443178..78548af1a 100644 --- a/src/keymap.lisp +++ b/src/keymap.lisp @@ -179,9 +179,9 @@ Example: (define-key *global-keymap* \"C-'\" 'list-modes)" (push *special-keymap* keymaps)) (delete-duplicates (nreverse keymaps)))) -(defun lookup-keybind (key) +(defun lookup-keybind (key &key (keymaps (all-keymaps))) (let (cmd) - (loop :for keymap :in (all-keymaps) + (loop :for keymap :in keymaps :do (setf cmd (keymap-find-keybind keymap key cmd))) cmd)) diff --git a/src/lem.lisp b/src/lem.lisp index ef5ce24dc..69ef3a7ec 100644 --- a/src/lem.lisp +++ b/src/lem.lisp @@ -222,7 +222,7 @@ See scripts/build-ncurses.lisp or scripts/build-sdl2.lisp" (apply-args args)) (defun run-editor-thread (initialize args finalize) - (bt:make-thread + (bt2:make-thread (lambda () (when initialize (funcall initialize)) (unwind-protect @@ -238,9 +238,9 @@ See scripts/build-ncurses.lisp or scripts/build-sdl2.lisp" :name "editor")) (defun find-editor-thread () - (find "editor" (bt:all-threads) + (find "editor" (bt2:all-threads) :test #'equal - :key #'bt:thread-name)) + :key #'bt2:thread-name)) (defun lem (&rest args) diff --git a/src/macros.lisp b/src/macros.lisp index 5da1d2c4c..a479f8435 100644 --- a/src/macros.lisp +++ b/src/macros.lisp @@ -32,12 +32,13 @@ #+sbcl (defmacro with-profile (&body body) - `(progn - (sb-profile:profile "LEM" "LEM-BASE" "LEM-INTERFACE") - ,@body - (with-debug-output ("PROFILE") - (sb-profile:report)) - (sb-profile:unprofile))) + (let ((names '("LEM" "LEM-CORE" "LEM-INTERFACE"))) + `(progn + (sb-profile:profile ,@names) + ,@body + (with-debug-output ("PROFILE") + (sb-profile:report)) + (sb-profile:unprofile ,@names)))) (defmacro with-editor-stream (() &body body) (alexandria:with-gensyms (stream) diff --git a/src/mode.lisp b/src/mode.lisp index 90fd94fb6..a44ae1f55 100644 --- a/src/mode.lisp +++ b/src/mode.lisp @@ -331,3 +331,5 @@ (defmacro with-major-mode (mode &body body) `(call-with-major-mode (current-buffer) ,mode (lambda () ,@body))) + +(defgeneric paste-using-mode (mode text)) diff --git a/src/site-init.lisp b/src/site-init.lisp index c2ac7e49e..75e34aa0b 100644 --- a/src/site-init.lisp +++ b/src/site-init.lisp @@ -2,6 +2,7 @@ (defvar *site-init-name* "lem-site-init") (defvar *site-init-comment ";; don't edit !!!") +(defvar *inits-directory-name* "lisp") (defun site-init-path () (let ((path (merge-pathnames (format nil "~A.asd" @@ -15,12 +16,12 @@ `(asdf:defsystem ,*site-init-name*))) path)) +(defun raw-init-files () + (directory (merge-pathnames "inits/*.lisp" (lem-home)))) + (defun site-init-list-inits () - (loop for i in (sort (mapcar #'pathname-name - (directory (merge-pathnames "inits/*.lisp" - (lem-home)))) - #'string<) - collect (list :file (format nil "inits/~A" i)))) + (loop for i in (sort (mapcar #'pathname-name (raw-init-files)) #'string<) + collect (list :file (format nil "inits/~A" i)))) (defun site-init () (with-open-file (i (site-init-path)) @@ -46,11 +47,16 @@ (defun load-site-init (&key force) (let* ((asdf:*central-registry* - (union (mapcar #'pathname - (mapcar #'directory-namestring - (directory - (merge-pathnames "**/*.asd" - (lem-home))))) + (union (remove-duplicates + (mapcar #'pathname + (mapcar #'directory-namestring + (directory + (merge-pathnames + "**/*.asd" + (pathname (str:concat + (directory-namestring (lem-home)) + *inits-directory-name* + (string (uiop:directory-separator-for-host))))))))) asdf:*central-registry* :test #'equal)) (system-name *site-init-name*) diff --git a/src/system.lisp b/src/system.lisp index 4c4ee69b6..90a0e66e1 100644 --- a/src/system.lisp +++ b/src/system.lisp @@ -29,8 +29,8 @@ (defun open-external-file (pathname) #+linux - (uiop:run-program (list "xdg-open" (namestring pathname))) + (uiop:launch-program (list "xdg-open" (namestring pathname))) #+darwin - (uiop:run-program (list "open" (namestring pathname))) + (uiop:launch-program (list "open" (namestring pathname))) #+windows - (uiop:run-program (list "explorer" (namestring pathname)) :ignore-error-status t)) + (uiop:launch-program (list "explorer" (namestring pathname)) :ignore-error-status t)) diff --git a/src/ui/theme-list.lisp b/src/ui/theme-list.lisp new file mode 100644 index 000000000..22d056748 --- /dev/null +++ b/src/ui/theme-list.lisp @@ -0,0 +1,42 @@ +(in-package :lem-core) + +(define-major-mode color-theme-selector-mode () + (:name "Themes" + :keymap *color-theme-selector-keymap*)) + +(define-key *color-theme-selector-keymap* "Return" 'color-theme-selector-select) + +(define-command color-theme-selector-select () () + (with-point ((point (current-point))) + (line-start point) + (let ((theme (text-property-at point 'theme))) + (load-theme theme)))) + +(define-command list-color-themes () () + (let* ((buffer (make-buffer "*Color Themes*")) + (point (buffer-point buffer)) + (dark-themes '()) + (light-themes '())) + (with-buffer-read-only buffer nil + (erase-buffer buffer) + (dolist (name (all-color-themes)) + (let ((theme (find-color-theme name))) + (if (eq :dark (get-color-theme-color theme :display-background-mode)) + (push (cons name theme) dark-themes) + (push (cons name theme) light-themes)))) + (loop :for (name . theme) :in (append dark-themes light-themes) + :do (let ((button-start (copy-point point :temporary)) + (theme-name name)) + (lem/button:insert-button + point + theme-name + (lambda () (load-theme theme-name)) + :attribute (make-attribute + :foreground (get-color-theme-color theme :foreground) + :background (get-color-theme-color theme :background))) + (put-text-property button-start point 'theme theme-name) + (insert-character point #\newline)))) + (buffer-start point) + (setf (buffer-read-only-p buffer) t) + (switch-to-buffer buffer) + (change-buffer-mode buffer 'color-theme-selector-mode))) diff --git a/src/window/side-window.lisp b/src/window/side-window.lisp index 25dbb2aaa..fda808cb4 100644 --- a/src/window/side-window.lisp +++ b/src/window/side-window.lisp @@ -11,9 +11,9 @@ (make-instance 'side-window :buffer buffer :x 0 - :y 1 + :y (topleft-window-y (current-frame)) :width width - :height (display-height) + :height (max-window-height (current-frame)) :use-modeline-p nil :background-color nil :border 0)) @@ -40,4 +40,4 @@ t))) (defun side-window-p (window) - (typep window 'side-window)) \ No newline at end of file + (typep window 'side-window)) diff --git a/src/window/window.lisp b/src/window/window.lisp index 6e5cd58e5..0753eae53 100644 --- a/src/window/window.lisp +++ b/src/window/window.lisp @@ -14,6 +14,9 @@ (defvar *last-focused-window* nil) +(defvar *switch-to-buffer-hook* '()) +(defvar *switch-to-window-hook* '()) + (defgeneric %delete-window (window)) (defgeneric window-parent (window) (:method (window) @@ -214,6 +217,7 @@ This is the content area in which the buffer is displayed, without any side marg (defun switch-to-window (new-window) (unless (eq (current-window) new-window) (run-hooks (window-leave-hook (current-window)) (current-window)) + (run-hooks *switch-to-window-hook* (current-window) new-window) (setf *last-focused-window* (current-window))) (setf (current-window) new-window)) @@ -456,7 +460,8 @@ You can pass in the optional argument WINDOW-LIST to replace the default (window-view window) width (- height - (if (window-use-modeline-p window) 1 0)))) + (if (window-use-modeline-p window) 1 0))) + (run-hooks *window-size-change-functions* window)) (defun window-move (window dx dy) (window-set-pos window @@ -828,6 +833,7 @@ You can pass in the optional argument WINDOW-LIST to replace the default (when (or (not-switchable-buffer-p (window-buffer (current-window))) (not-switchable-buffer-p buffer)) (editor-error "This buffer is not switchable")) + (run-hooks *switch-to-buffer-hook* buffer) (run-hooks (window-switch-to-buffer-hook (current-window)) buffer) (%switch-to-buffer buffer record move-prev-point)) diff --git a/tests/buffer/internal.lisp b/tests/buffer/internal.lisp new file mode 100644 index 000000000..53a42dea2 --- /dev/null +++ b/tests/buffer/internal.lisp @@ -0,0 +1,81 @@ +(defpackage :lem-tests/buffer/internal + (:use :cl + :rove)) +(in-package :lem-tests/buffer/internal) + +(defun check-corruption (buffer) + (handler-case (lem/buffer/internal:check-buffer-corruption buffer) + (lem/buffer/internal:corruption-warning () + (fail "corruption")))) + +(defun collect-line-plist (buffer) + (loop :for line := (lem/buffer/internal::point-line (lem:buffer-start-point buffer)) + :then (lem/buffer/line:line-next line) + :while line + :collect (lem/buffer/line:line-plist line))) + +(deftest insert-newline-test + ;; Arrange + (let* ((buffer (lem:make-buffer "test" :temporary t)) + (point (lem:buffer-point buffer))) + (lem:insert-string point "a" :key1 100) + (lem:insert-string point "bcdefg" :key2 200) + (lem:insert-string point "hijklmnopqrstuvwxyz" :key3 300) + + ;; Act + (lem:move-to-line point 1) + (lem:move-to-column point 2) + (lem:insert-character point #\newline) + + (lem:move-to-line point 2) + (lem:move-to-column point 4) + (lem:insert-character point #\newline) + + (lem:move-to-line point 3) + (lem:move-to-column point 10) + (lem:insert-character point #\newline) + + ;; Assertions + (check-corruption buffer) + (ok (= 4 (lem:buffer-nlines buffer))) + (ok (equal "ab +cdef +ghijklmnop +qrstuvwxyz" + (lem:buffer-text buffer))) + (ok (equal '((:KEY3 NIL :KEY2 ((1 2 200)) :KEY1 ((0 1 100 NIL))) + (:KEY3 NIL) + (:KEY3 ((1 10 300))) + NIL) + (collect-line-plist buffer))))) + +(deftest undo-redo + (let* ((buffer (lem:make-buffer "test" :temporary t)) + (point (lem:buffer-point buffer))) + (lem:insert-string point "Hello") + (lem:buffer-undo-boundary buffer) + (lem:insert-string point " World") + (lem:buffer-undo-boundary buffer) + (lem:buffer-undo point) + (ok (equal "Hello" (lem:buffer-text buffer))) + (lem:buffer-redo point) + (ok (equal "Hello World" (lem:buffer-text buffer))) + + (check-corruption buffer))) + +(deftest |`buffer-end-point` points to the end of the buffer| + ;; Arrange + (let* ((buffer (lem:make-buffer "test" :temporary t)) + (point (lem:buffer-point buffer))) + (lem:insert-string point "aaaaaaaaaa") + + ;; Act + (lem:move-to-line point 1) + (lem:move-to-column point 5) + (lem:delete-character point 10) + + ;; Assertion + (let ((end-point (lem:buffer-end-point buffer))) + (ok (= 5 (lem:point-charpos end-point))) + (ok (= 1 (lem:line-number-at-point point)))) + (check-corruption buffer))) diff --git a/tests/common/history.lisp b/tests/common/history.lisp index 241bc624e..ee7349eea 100644 --- a/tests/common/history.lisp +++ b/tests/common/history.lisp @@ -18,3 +18,31 @@ (testing "next-history" (ok (equal '("bar" t) (multiple-value-list (next-history history)))) (ok (null (next-history history)))))) + +(deftest add-history-test + (let ((history (make-history))) + (testing "basic add-history" + (add-history history "first") + (add-history history "second") + (ok (equal '("first" "second") (history-data-list history)))) + + (testing "without allow-duplicates" + (add-history history "third" :allow-duplicates nil) + (add-history history "second" :allow-duplicates nil) + (ok (equal '("first" "second" "third") (history-data-list history)))) + + (testing "with allow-duplicates" + (add-history history "second" :allow-duplicates t) + (ok (equal '("first" "second" "third" "second") (history-data-list history)))) + + (testing "with move-to-top" + (add-history history "first" :move-to-top t) + (ok (equal '("second" "third" "second" "first") (history-data-list history)))) + + (testing "limit functionality" + (let ((limited-history (make-history :limit 3))) + (add-history limited-history "one") + (add-history limited-history "two") + (add-history limited-history "three") + (add-history limited-history "four") + (ok (equal '("two" "three" "four") (history-data-list limited-history))))))) diff --git a/tests/killring.lisp b/tests/killring.lisp index 4ad132f7b..f1f345047 100644 --- a/tests/killring.lisp +++ b/tests/killring.lisp @@ -16,11 +16,11 @@ (lem-core::*killring* killring)) (ok (equal '("baz" (:test)) (multiple-value-list (yank-from-clipboard-or-killring))))) - + ;; clipboard enabled (let ((lem-core::*enable-clipboard-p* t) (lem-core::*killring* killring) (expected-result "In LEM we trust.")) (copy-to-clipboard-with-killring expected-result) (ok (equal expected-result - (yank-from-clipboard-or-killring))))))) \ No newline at end of file + (yank-from-clipboard-or-killring)))))))