branch: elpa/swift-mode commit 25944c2a98a8f78453e14591884a940c64740347 Author: taku0 <mxxouy6x3m_git...@tatapa.org> Commit: taku0 <mxxouy6x3m_git...@tatapa.org>
Add test for beginning/end-of-defun --- swift-mode-beginning-of-defun.el | 12 +- swift-mode-lexer.el | 8 +- swift-mode.el | 2 + .../beginning-of-defun/beginning-of-defun.swift | 319 +++++++++++++++++++++ test/swift-mode-test-beginning-of-defun.el | 242 ++++++++++++++++ test/swift-mode-test-indent.el | 2 +- test/swift-mode-test.el | 5 +- 7 files changed, 579 insertions(+), 11 deletions(-) diff --git a/swift-mode-beginning-of-defun.el b/swift-mode-beginning-of-defun.el index 1a9d9a3..0bba2b5 100644 --- a/swift-mode-beginning-of-defun.el +++ b/swift-mode-beginning-of-defun.el @@ -332,23 +332,23 @@ Intended for internal use." '(\; implicit-\; } anonymous-function-parameter-in outside-of-buffer))) (swift-mode:pseudo-implicit-semicolon-p token)))) - (while (eq (swift-mode:token:type - (save-excursion (swift-mode:forward-token))) - '\;) - (setq token (swift-mode:forward-token))) (if (memq (swift-mode:token:type token) '(\; anonymous-function-parameter-in)) (goto-char (swift-mode:token:end token)) (goto-char (swift-mode:token:start token))) + (while (eq (swift-mode:token:type + (save-excursion (swift-mode:forward-token))) + '\;) + (setq token (swift-mode:forward-token))) (cond ((eq (swift-mode:token:type token) 'outside-of-buffer) (forward-comment (- (point))) - (when (< (point) pos) + (when (<= (point) pos) (goto-char (swift-mode:token:end token))) token) ((eq (swift-mode:token:type token) '}) (forward-comment (- (point))) - (if (< (point) pos) + (if (<= (point) pos) (progn (goto-char (swift-mode:token:end token)) (swift-mode:end-of-statement)) diff --git a/swift-mode-lexer.el b/swift-mode-lexer.el index 6a47238..ed9e5fd 100644 --- a/swift-mode-lexer.el +++ b/swift-mode-lexer.el @@ -1252,7 +1252,9 @@ If this line ends with a single-line comment, goto just before the comment." ;; Proceed to the end of the comment. (goto-char (swift-mode:chunk:start chunk)) (forward-comment 1) - (end-of-line))))) + (end-of-line) + (when (and (eobp) (swift-mode:chunk-after)) + (goto-char (swift-mode:chunk:start (swift-mode:chunk-after)))))))) ;;; Comment or string chunks @@ -1301,8 +1303,8 @@ If this line ends with a single-line comment, goto just before the comment." If the cursor is outside of strings and comments, return nil. If PARSER-STATE is given, it is used instead of (syntax-ppss)." - (unless parser-state - (setq parser-state (syntax-ppss))) + (when (or (null parser-state) (number-or-marker-p parser-state)) + (setq parser-state (save-excursion (syntax-ppss parser-state)))) (cond ((eq (nth 3 parser-state) t) (swift-mode:chunk 'multiline-string (nth 8 parser-state))) diff --git a/swift-mode.el b/swift-mode.el index 25f5c8f..cbd3257 100644 --- a/swift-mode.el +++ b/swift-mode.el @@ -97,6 +97,8 @@ See `forward-sexp for ARG." (setq arg (or arg 1)) + (when (swift-mode:chunk-after) + (goto-char (swift-mode:chunk:start (swift-mode:chunk-after)))) (if (< 0 arg) (while (< 0 arg) (while (eq (swift-mode:token:type (swift-mode:forward-sexp-1)) diff --git a/test/swift-files/beginning-of-defun/beginning-of-defun.swift b/test/swift-files/beginning-of-defun/beginning-of-defun.swift new file mode 100644 index 0000000..e4e1dfb --- /dev/null +++ b/test/swift-files/beginning-of-defun/beginning-of-defun.swift @@ -0,0 +1,319 @@ +// Foo bar baz. + +// Import declarations + +/*{*//* aaa */ /* bbb */@Foo import Foundation/*}*/ +/*{*/@Foo +import Foundation/*}*/ + +/*{*/class Foo { + /*{*/@Foo import Foundation/*}*/ + /*{*/@Foo + import Foundation/*}*/ +}/*}*/ + +// Constant/variable declarations + +/*{*/let x = foo()/*}*/ +/*{*/@Foo +let + y + = + bar()/*}*/ + +/*{*/class Foo { + /*{*/let x = foo()/*}*/ + /*{*/@Foo + public + let + y + = + bar()/*}*/ + + /*{*/var a: Int { + return 0 + }/*}*/ + + /*{*/var a: Int { + /*{*/@Foo + get { + return 0 + }/*}*/ + + /*{*/@Foo + set { + foo() + }/*}*/ + }/*}*/ + + /*{*/var a = 0 { + /*{*/@Foo + willSet { + foo() + }/*}*/ + + /*{*/@Foo + didSet { + foo() + }/*}*/ + }/*}*/ + + /*{*/func foo() { + let x = foo() + let + y + = + bar() + while + let + x + = + xx, + var + y + = + yy, + case + ( + a, + b + ) + = + ab { + foo() + foo() + } + }/*}*/ +}/*}*/ + +// Type alias declarationss + +/*{*/@Foo typealias A = B/*}*/ + +/*{*/@Foo +typealias + A + = + B/*}*/ + +/*{*/class Foo { + /*{*/@Foo typealias A = B/*}*/ + + /*{*/@Foo + public + typealias + A + = + B/*}*/ +}/*}*/ + +// Function declarations + +/*{*/func foo() { +}/*}*/ + +/*{*/@Foo +func +foo() { +}/*}*/ + +/*{*/class Foo { + /*{*/func foo() { + }/*}*/ + + /*{*/@Foo + public + func + foo<A + : + X, + B, + C>( + a a + : + (Int -> [Int]) + = + { + x + in + [ + x + ] + } + ) + throws + -> + @Foo + [ + A + ] + where + A + : + X + , + B + == + Int + { + }/*}*/ +}/*}*/ + +// Enum declarations + +/*{*/enum Foo<A> where A: B { + /*{*/case Foo(a: Int)/*}*/ + /*{*/case Bar(b: Int), Baz/*}*/ + /*{*/case + A( + b + : + Int) + , + @Foo + indirect + B + = + 0/*}*/ + + /*{*/func foo() -> a { + switch this { + case .Foo: + return a + case + let .Bar(a): + return a + case + .Baz(var a): + return a + } + }/*}*/ +}/*}*/ + + +// Struct declarations + +/*{*/struct Foo { +}/*}*/ + +// Class declarations + +/*{*/class Foo { +}/*}*/ + +/*{*/@Foo +public + final + class + Foo<A + : + X + , + B + , + C> + : + X + where + A + : + X + , + B + == + Int +{ + /*{*/class Foo { + }/*}*/ +}/*}*/ + +// Protocol declarations + +/*{*/protocol Foo { + /*{*/var x: Int { + /*{*/get/*}*/ + /*{*/set/*}*/ + }/*}*/ + /*{*/func foo()/*}*/ + + /*{*/associatedtype + A + : + B + = + C + where + A + : + D, + A + == + E/*}*/ +}/*}*/ + +// Extension declarations +/*{*/extension Foo: AAA { +}/*}*/ + +// Operator declarations +/*{*/prefix + operator + +++/*}*/ +/*{*/postfix + operator + +++/*}*/ +/*{*/infix + operator + +++ + : + AAA/*}*/ + +// Precedence group declarations +/*{*/precedencegroup Foo { + higherThan: AAA, BBB, CCC + lowerThan: DDD, EEE, FFF + assignment: false + associativity: left +}/*}*/ + +/*{*/class Foo { + // Initializer declarations + /*{*/init() { + `init`() { + } + }/*}*/ + + // Deinitializer declarations + /*{*/deinit() { + }/*}*/ + + // Subscript declarations + /*{*/subscript(x: Int) { + }/*}*/ +}/*}*/ + +// Multiple declaratoins in single line + +/*{*/func foo(){};/*}*/ /*{*/func foo(){/*{*/func foo(){}/*}*/};/*}*//*{*/func foo(){} ;/*}*/ /*{*/func foo() {} /* */ ;/*}*/ /*{*//* */ func foo() {}/*}*/ + +// Strings and comments + +/*{*/let x = """ + class Foo {} + \( + { () in + /*{*/class Foo { + }/*}*/ + return 0 + }() + ) + """/*}*/ + +// class Foo {} + +/* + class Foo { + } + */ + +// Foo bar baz. diff --git a/test/swift-mode-test-beginning-of-defun.el b/test/swift-mode-test-beginning-of-defun.el new file mode 100644 index 0000000..129170f --- /dev/null +++ b/test/swift-mode-test-beginning-of-defun.el @@ -0,0 +1,242 @@ +;;; swift-mode-test-beginning-of-defun.el --- Test for swift-mode: beginning-of-defun -*- lexical-binding: t -*- + +;; Copyright (C) 2017 taku0 + +;; Authors: taku0 (http://github.com/taku0) +;; +;; Version: 4.0.0 +;; Package-Requires: ((emacs "24.4") (seq "2.3")) +;; Keywords: languages swift +;; URL: https://github.com/swift-emacs/swift-mode + +;; This file is not part of GNU Emacs. + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <http://www.gnu.org/licenses/>. + +;;; Commentary: + +;; Test for swift-mode: beginning-of-defun. +;; Execute swift-mode:run-test:beginning-of-defun interactively or in batch +;; mode. + +;;; Code: + +(require 'swift-mode) +(require 'swift-mode-beginning-of-defun) +(require 'seq) + +(defun swift-mode:run-test:beginning-of-defun + (&optional error-buffer error-counts progress-reporter) + "Run `beginning-of-defun' test for `swift-mode'. + +ERROR-BUFFER is the buffer to output errors. +ERROR-COUNTS is a association list holding counts of errors. Updated +destructively. +PROGRESS-REPORTER is the progress-reporter." + (interactive) + (if (not swift-mode:test:running) + (swift-mode:run-test '(swift-mode:run-test:beginning-of-defun)) + (let ((current-line 0) + expected-positions-desc + expected-positions-asc + tests) + (setq default-directory + (concat (file-name-as-directory swift-mode:test:basedir) + (file-name-as-directory "swift-files") + "beginning-of-defun")) + ;; TODO forward-sentence, backward-sentence + (dolist (swift-file (file-expand-wildcards "*.swift")) + (redisplay) + (with-temp-buffer + (switch-to-buffer (current-buffer)) + (insert-file-contents-literally swift-file) + (swift-mode) + (setq expected-positions + (swift-mode:parse-beginning-of-defun-test-file)) + (setq expected-positions-desc + (mapcar (lambda (p) + (list + (nth 0 p) + (nth 2 p) + (nth 4 p))) + expected-positions)) + (setq expected-positions-asc + (mapcar (lambda (p) + (list + (nth 0 p) + (nth 1 p) + (nth 3 p))) + (reverse expected-positions))) + (setq test-parameters + (list + (list + expected-positions-desc + #'< + (lambda () + (swift-mode:beginning-of-defun) + (skip-syntax-forward " ")) + 'beginning-of-defun) + (list + expected-positions-asc + #'> + #'swift-mode:end-of-defun + 'end-of-defun))) + (setq current-line 0) + (while (not (eobp)) + (when (not noninteractive) + (progress-reporter-update progress-reporter)) + (setq current-line (1+ current-line)) + (when (looking-at ".*//.*swift-mode:test:eval\\(.*\\)") + (eval-region (match-beginning 1) (match-end 1))) + + (dolist (test-parameter test-parameters) + (let* ((status (apply + #'swift-mode:test-current-line-beginning-of-defun + swift-file + current-line + error-buffer + test-parameter)) + (count-assoc (assq status error-counts))) + (setcdr count-assoc (1+ (cdr count-assoc))))) + (forward-line))))))) + +(defun swift-mode:parse-beginning-of-defun-test-file () + "Parse the current buffer as a test file and return its structure. + +The result is a list of remarkable tokens in descendant order. A remarkable +token is a list with the follwing elements: + +1. Type; one of `beginning-of-defun', `end-of-defun', `{', or `}' +2. Start position +3. End position +4. Nesting level at the start position +5. Nesting level at the end position + +`beginning-of-defun' and `end-of-defun' are represented as /*{*/ and /*}*/, +respectively, in the test file, and removed from the buffer. + +`{' and `}' includes square brackets and parentheses." + (save-excursion + (goto-char (point-min)) + (let ((expected-positions + (list (list 'beginning-of-defun (point) (point) 0 0))) + (depth 0) + (pattern (mapconcat #'regexp-quote + '("/*{*/" "/*}*/" "{" "}" "[" "]" "(" ")") + "\\|")) + match-string + match-beginning + match-end) + (while (search-forward-regexp pattern nil t) + (setq match-string (match-string-no-properties 0)) + (setq match-beginning (match-beginning 0)) + (setq match-end (match-end 0)) + (cond + ((equal match-string "/*{*/") + (add-to-list 'expected-positions + (list 'beginning-of-defun + match-beginning match-beginning + depth depth)) + (replace-match "")) + ((equal match-string "/*}*/") + (add-to-list 'expected-positions + (list 'end-of-defun + match-beginning match-beginning + depth depth)) + (replace-match "")) + ((and (member match-string '("{" "[" "(")) + (not (swift-mode:chunk-after match-beginning))) + (setq depth (1+ depth)) + (add-to-list 'expected-positions + (list '{ match-beginning match-end (1- depth) depth))) + ((and (member match-string '("}" "]" ")")) + (not (swift-mode:chunk-after match-end))) + (setq depth (1- depth)) + (add-to-list 'expected-positions + (list '} match-beginning match-end (1+ depth) depth))))) + (goto-char (point-max)) + (add-to-list 'expected-positions + (list 'end-of-defun (point) (point) depth depth)) + expected-positions))) + +(defun swift-mode:test-current-line-beginning-of-defun + (swift-file + current-line + error-buffer + expected-positions + less-than-function + beginning-of-thing-function + boundary-symbol) + "Run `beginning-of-defun' test for `swift-mode' on current line. + +SWIFT-FILE is the filename of the current test case. +CURRENT-LINE is the current line number. +ERROR-BUFFER is the buffer to output errors. +EXPECTED-POSITIONS is a list of remarkable tokens +\(see `swift-mode:parse-beginning-of-defun-test-file'). +LESS-THAN-FUNCTION is a function returns non-nil iff the firt argument is +before (or after for `end-of-defun' test) the second argument. +BEGINNING-OF-THING-FUNCTION is a function goes to the boundary, that is the +beginning of a defun or the end of the defun.. +BOUNDARY-SYMBOL is the type of expected remarkable token, like +`beginning-of-defun' or `end-of-defun`'" + (forward-line 0) + (let ((status 'ok) + depth + expected-positions-before-point + expected-position + actual-position) + (while (eq status 'ok) + (setq expected-positions-before-point + (seq-drop-while + (lambda (position) + (funcall less-than-function (point) (nth 1 position))) + expected-positions)) + (setq depth (or (nth 2 (car expected-positions-before-point)) 0)) + (setq expected-position + (nth 1 (seq-find + (lambda (position) + (setq depth (min depth (nth 2 position))) + (and + (eq (nth 0 position) boundary-symbol) + (funcall less-than-function (nth 1 position) (point)) + (<= (nth 2 position) depth))) + expected-positions-before-point + (list nil (point-min) nil)))) + (setq actual-position (save-excursion + (funcall beginning-of-thing-function) + (point))) + (when (/= expected-position actual-position) + (setq status 'error) + (swift-mode:show-error + error-buffer swift-file current-line + "error" + (concat + (symbol-name boundary-symbol) + ": at " + (prin1-to-string (point)) + ", expected " + (prin1-to-string expected-position) + " but " + (prin1-to-string actual-position)))) + (if (eolp) + (setq status 'done) + (forward-char))) + (when (eq status 'done) + (setq status 'ok)) + status)) + +(provide 'swift-mode-test-beginning-of-defun) + +;;; swift-mode-test-beginning-of-defun.el ends here diff --git a/test/swift-mode-test-indent.el b/test/swift-mode-test-indent.el index a48236c..f1a0018 100644 --- a/test/swift-mode-test-indent.el +++ b/test/swift-mode-test-indent.el @@ -110,7 +110,7 @@ ERROR-BUFFER is the buffer to output errors." (if known-bug "warning" "error") (concat (if known-bug "(knwon bug) " "") - "expected " + "indent: expected " (prin1-to-string original-indent) " but " (prin1-to-string computed-indent)))) diff --git a/test/swift-mode-test.el b/test/swift-mode-test.el index 99410ba..8922da2 100644 --- a/test/swift-mode-test.el +++ b/test/swift-mode-test.el @@ -32,6 +32,7 @@ ;;; Code: (require 'swift-mode-test-indent) +(require 'swift-mode-test-beginning-of-defun) (defvar swift-mode:test:basedir (file-name-directory (or load-file-name buffer-file-name))) @@ -48,7 +49,9 @@ Return the error-buffer" (erase-buffer) (current-buffer)) -(defvar swift-mode:tests '(swift-mode:run-test:indent)) +(defvar swift-mode:tests + '(swift-mode:run-test:indent + swift-mode:run-test:beginning-of-defun)) (defun swift-mode:run-test (&optional tests) "Run TESTS for `swift-mode'."