branch: elpa/smartparens commit c2b2e603f904baff69e6606d11d5961ca298d332 Merge: 3ee3baf41f 7e5f169879 Author: Matus Goljer <matus.gol...@gmail.com> Commit: GitHub <nore...@github.com>
Merge pull request #1125 from woolsweater/swift-mode Add basic 'swift-mode' support --- Cask | 1 + smartparens-config.el | 1 + smartparens-swift.el | 117 ++++++++++++++++++++++++++++++++++++++ test/smartparens-swift-test.el | 126 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 245 insertions(+) diff --git a/Cask b/Cask index 04bbff4b9d..bc87d58c78 100644 --- a/Cask +++ b/Cask @@ -16,6 +16,7 @@ (depends-on "racket-mode") (depends-on "scala-mode") (depends-on "rust-mode") + (depends-on "swift-mode") (depends-on "auctex") (depends-on "clojure-mode") (depends-on "lua-mode") diff --git a/smartparens-config.el b/smartparens-config.el index 6e17e4b4b6..4ae1293ef7 100644 --- a/smartparens-config.el +++ b/smartparens-config.el @@ -123,6 +123,7 @@ ID, ACTION, CONTEXT." (eval-after-load 'rust-mode '(require 'smartparens-rust)) (eval-after-load 'rustic '(require 'smartparens-rust)) (eval-after-load 'scala-mode '(require 'smartparens-scala)) +(eval-after-load 'swift-mode '(require 'smartparens-swift)) (eval-after-load 'tex-mode '(require 'smartparens-latex)) (eval-after-load 'text-mode '(require 'smartparens-text)) (eval-after-load 'tuareg '(require 'smartparens-ml)) diff --git a/smartparens-swift.el b/smartparens-swift.el new file mode 100644 index 0000000000..a1efab109d --- /dev/null +++ b/smartparens-swift.el @@ -0,0 +1,117 @@ +;;; smartparens-swift.el --- Additional configuration for Swift language buffers. -*- lexical-binding: t; -*- + +;; Copyright (C) 2015 Wilfred Hughes, +;; 2022 Josh Caswell + +;; Created: 4 April 2022 +;; Keywords: abbrev convenience editing +;; URL: https://github.com/Fuco1/smartparens + +;; This file is not part of GNU Emacs. + +;;; License: + +;; This file is part of Smartparens. + +;; Smartparens is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; Smartparens is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with Smartparens. If not, see <http://www.gnu.org/licenses/>. + +;;; Commentary: + +;; This file provides some additional configuration for Swift. To use +;; it, simply add: +;; +;; (require 'smartparens-config) +;; +;; alternatively, you can explicitly load these preferences: +;; +;; (require 'smartparens-swift) +;; +;; in your configuration. + +;; For more info, see github readme at +;; https://github.com/Fuco1/smartparens + +;;; Code: +(require 'smartparens) + +(declare-function swift-mode "swift-mode") + +(defun sp-swift-skip-match-angle-bracket (_ms _mb me) + "Non-nil if we should ignore the bracket as valid delimiter." + (save-excursion + (goto-char me) + (let ((on-fn-return-type + (sp--looking-back-p (rx "->") nil)) + (on-range-operator + (sp--looking-back-p (rx "..<") nil)) + (on-comparison + (sp--looking-back-p (rx (or + (seq space "<") + (seq space ">") + (seq space "<<") + (seq space ">>"))) + nil))) + (or on-comparison on-fn-return-type on-range-operator)))) + +(defun sp-swift-filter-angle-brackets (_id action context) + "Non-nil if we should allow ID's ACTION in CONTEXT for angle brackets." + ;; See the docstring for `sp-pair' for the possible values of ID, + ;; ACTION and CONTEXT. + (cond + ;; Inside strings, don't do anything with < or >. + ((eq context 'string) + nil) + ((or (eq context 'comment) + (eq context 'code)) + (let ((on-fn-return-type + (looking-back (rx "->") nil)) + (on-range-operator + (looking-back (rx "..<") nil)) + (on-comparison + (looking-back (rx (or + (seq space "<") + (seq space ">") + (seq space "<<") + (seq space ">>"))) + nil))) + (cond + ;; Only insert a matching > if we're not looking at one of the operators. + ((eq action 'insert) + (and (not on-comparison) (not on-fn-return-type) (not on-range-operator))) + ;; Allow wrapping in a pair if the region is active and we're not on a + ;; range operator. + ((eq action 'wrap) + (not on-range-operator)) + ;; When pressing >, autoskip if we're not looking at one of the + ;; operators. + ((eq action 'autoskip) + (and (not on-comparison) (not on-fn-return-type) (not on-range-operator))) + ;; Allow navigation, highlighting and strictness checks if it's + ;; not one of the operators. + ((eq action 'navigate) + (and (not on-comparison) (not on-fn-return-type) (not on-range-operator)))))))) + +(sp-with-modes '(swift-mode) + (sp-local-pair "<" ">" + :when '(sp-swift-filter-angle-brackets) + :skip-match 'sp-swift-skip-match-angle-bracket) + (sp-local-pair "\"\"\"" "\"\"\"")) + +;; Swift has no sexp suffices. This fixes slurping +;; (|foo).bar -> (foo.bar) +(add-to-list 'sp-sexp-suffix (list #'swift-mode 'regexp "")) + +(provide 'smartparens-swift) + +;;; smartparens-swift.el ends here diff --git a/test/smartparens-swift-test.el b/test/smartparens-swift-test.el new file mode 100644 index 0000000000..a9eefd8252 --- /dev/null +++ b/test/smartparens-swift-test.el @@ -0,0 +1,126 @@ +(require 'swift-mode) +(require 'smartparens) + +(ert-deftest sp-test-swift-kill-first-line () + "Ensure we can kill words on the first line. +Regression test." + (sp-test-with-temp-buffer "extern|" + (swift-mode) + (sp-backward-kill-word 1) + (should (equal (buffer-string) "")))) + +(ert-deftest sp-test-swift-pair-angle-bracket () + "When typing < we should insert the matching pair +\(when appropriate\)." + (sp-test-with-temp-buffer "Result|" + (swift-mode) + (execute-kbd-macro "<") + ;; We should have inserted a pair. + (should (equal (buffer-string) "Result<>")))) + +(ert-deftest sp-test-swift-forward-angle-bracket () + "< and > are usually brackets in Swift." + (sp-test-with-temp-buffer "struct Foo { + baz: Baz|<T> +}" + (swift-mode) + (sp-forward-sexp) + ;; We should have moved over the closing >. + (should (looking-at "\n")))) + +(ert-deftest sp-test-swift-less-than () + "When using < to compare, don't insert >." + (sp-test-with-temp-buffer "if x |" + (swift-mode) + (execute-kbd-macro "<") + (should (equal (buffer-string) "if x <")))) + +(ert-deftest sp-test-swift-left-shift () + "When using << for a left shift, don't insert >." + (sp-test-with-temp-buffer "if x <|" + (swift-mode) + (execute-kbd-macro "<") + (should (equal (buffer-string) "if x <<")))) + +(ert-deftest sp-test-swift-left-shift-then-function () + "We should still be able to insert -> after a left shift." + (sp-test-with-temp-buffer "let y: UInt64 = 1 << 2; + +func foo(x: UInt64) -| + +func bar(x: UInt64) -> Bool { + true +} +" + (swift-mode) + (smartparens-strict-mode) + (execute-kbd-macro ">") + (should (equal (buffer-substring (line-beginning-position) (line-end-position)) + "func foo(x: UInt64) ->")))) + +(ert-deftest sp-test-swift-delete-comparison () + "We should be able to delete comparisons, even in strict mode." + (sp-test-with-temp-buffer "a < b; b >|" + (swift-mode) + (smartparens-strict-mode) + (execute-kbd-macro (kbd "<backspace>")) + (should (equal (buffer-string) "a < b; b ")))) + +(ert-deftest sp-test-swift-pair-angle-bracket-in-constructor-call () + "Pair < when parameterizing constructor calls." + (sp-test-with-temp-buffer "Array|" + (swift-mode) + (execute-kbd-macro "<") + ;; We should have inserted a pair. + (should (equal (buffer-string) "Array<>")))) + +(ert-deftest sp-test-swift-pair-autoskip-closing-bracket () + "Autoskip a matching >." + (sp-test-with-temp-buffer "Array<T|>" + (swift-mode) + (execute-kbd-macro ">") + ;; We should have skipped the closing bracket. + (should (equal (buffer-string) "Array<T>")))) + +(ert-deftest sp-test-swift-pair-insert-and-autoskip-closing-bracket () + "Inserting multiple > and closing them." + (sp-test-with-temp-buffer "Optional|" + (swift-mode) + (execute-kbd-macro "<Foo<Bar<T>>>") + ;; We should have inserted a pair without an extra chevron. + (should (equal (buffer-string) "Optional<Foo<Bar<T>>>")))) + +(ert-deftest sp-test-swift-insert-range-operator () + "Inserting a range operator." + (sp-test-with-temp-buffer "foo..|" + (swift-mode) + (execute-kbd-macro "<bar") + (should (equal (buffer-string) "foo..<bar")))) + +;; #793 +(ert-deftest sp-test-swift-skip-forward-over-return-type () + "Moving forward over a function's return type." + (sp-test-with-temp-buffer "func foo() |-> UInt32" + (swift-mode) + (sp-forward-sexp) + (execute-kbd-macro "{") + (sp-buffer-equals "func foo() -> UInt32{|}"))) + +;; #793 +(ert-deftest sp-test-swift-skip-backward-over-return-type () + "Moving backward over a function's return type." + (sp-test-with-temp-buffer "foo() -> |UInt32 {}" + (swift-mode) + (smartparens-strict-mode 1) + (sp-backward-sexp) + (execute-kbd-macro "func ") + (sp-buffer-equals "func |foo() -> UInt32 {}"))) + +;; #793 +(ert-deftest sp-test-swift-kill-defun () + "Deleting a region containing a Swift function definition." + (sp-test-with-temp-buffer "|func foo() ->UInt32 {}" + (swift-mode) + (mark-whole-buffer) + (call-interactively 'sp-kill-region) + (should (equal (buffer-string) ""))))