From ce1ed9c8116e280c69f04a455176f6097ca0855c Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Mon, 15 Aug 2016 11:52:32 +0000 Subject: [PATCH] Handle C++11 lambda functions. * lisp/progmodes/cc-engine.el (c-looking-at-inexpr-block): Enhance also to handle C++ lambda functions. (c-looking-at-c++-lambda-capture-list): New function. * lisp/progmodes/cc-fonts.el (c-font-lock-declarations): Recognize the parameter list of a lambda function and set `context' and `c-restricted-<>-arglists' suitably for it. (c-font-lock-c++-lambda-captures): New function. (c-complex-decl-matchers): Insert c-font-lock-c++-lambda-captures into it. * lisp/progmodes/cc-langs.el (c-pre-lambda-tokens, c-pre-lambda-tokens-re): New language constants/variables. (c-paren-nontype-kwds): Include "noexcept" in the C++ value. * lisp/progmodes/cc-mode.el (c-fl-decl-start): Handle being in a C++ lambda function capture list. --- lisp/progmodes/cc-engine.el | 85 +++++++++++++++++++++++++++++++------ lisp/progmodes/cc-fonts.el | 101 ++++++++++++++++++++++++++++++++++++++++++++ lisp/progmodes/cc-langs.el | 24 ++++++++++- lisp/progmodes/cc-mode.el | 46 ++++++++++++++++++++ 4 files changed, 243 insertions(+), 13 deletions(-) diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el index e22b98dbc4d..4a29896b4a5 100644 --- a/lisp/progmodes/cc-engine.el +++ b/lisp/progmodes/cc-engine.el @@ -10005,12 +10005,27 @@ comment at the start of cc-engine.el for more info." ;; This function might do hidden buffer changes. (save-excursion - (let ((res 'maybe) passed-paren + (let ((res 'maybe) (passed-bracket-pairs 0) bracket-pos passed-paren + haskell-op-pos (closest-lim (or containing-sexp lim (point-min))) ;; Look at the character after point only as a last resort ;; when we can't disambiguate. (block-follows (and (eq (char-after) ?{) (point)))) + ;; Search for a C++11 "->" which suggests a lambda declaration. + (when (and (c-major-mode-is 'c++-mode) + (setq haskell-op-pos + (save-excursion + (while + (progn + (c-syntactic-skip-backward "^;=}>" closest-lim t) + (and (eq (char-before) ?>) + (c-backward-token-2) + (not (looking-at c-haskell-op-re))))) + (and (looking-at c-haskell-op-re) + (point))))) + (goto-char haskell-op-pos)) + (while (and (eq res 'maybe) (progn (c-backward-syntactic-ws) (> (point) closest-lim)) @@ -10048,6 +10063,11 @@ comment at the start of cc-engine.el for more info." (zerop (c-forward-token-2 1 t))) (eq (char-after) ?\()))) (cons 'inexpr-class (point)))) + ((c-keyword-member kw-sym 'c-paren-any-kwds) ; e.g. C++11 "throw" or "noexcept" + (setq passed-paren nil) + (setq passed-bracket-pairs 0) + (setq bracket-pos nil) + 'maybe) ((c-keyword-member kw-sym 'c-inexpr-block-kwds) (when (not passed-paren) (cons 'inexpr-statement (point)))) @@ -10062,20 +10082,49 @@ comment at the start of cc-engine.el for more info." (if (looking-at "\\s(") (if passed-paren - (if (and (eq passed-paren ?\[) - (eq (char-after) ?\[)) - ;; Accept several square bracket sexps for - ;; Java array initializations. - 'maybe) - (setq passed-paren (char-after)) + (cond + ((and (eq passed-paren ?\[) + (eq (char-after) ?\[) + (not (eq (char-after (1+ (point))) ?\[))) ; C++ attribute. + ;; Accept several square bracket sexps for + ;; Java array initializations. + (setq passed-bracket-pairs (1+ passed-bracket-pairs)) + 'maybe) + ((and (eq passed-paren ?\() + (eq (char-after) ?\[) + (not (eq (char-after (1+ (point))) ?\[)) + (eq passed-bracket-pairs 0)) + ;; C++11 lambda function declaration + (setq passed-bracket-pairs 1) + (setq bracket-pos (point)) + 'maybe) + (t nil)) + (when (not (looking-at "\\[\\[")) + (setq passed-paren (char-after)) + (when (eq passed-paren ?\[) + (setq passed-bracket-pairs 1) + (setq bracket-pos (point)))) 'maybe) 'maybe)))) (if (eq res 'maybe) - (when (and c-recognize-paren-inexpr-blocks - block-follows - containing-sexp - (eq (char-after containing-sexp) ?\()) + (cond + ((and (c-major-mode-is 'c++-mode) + block-follows + (eq passed-bracket-pairs 1) + (save-excursion + (goto-char bracket-pos) + (or (<= (point) (or lim (point-min))) + (progn + (c-backward-token-2 1 nil lim) + (and + (not (c-on-identifier)) + (not (looking-at c-opt-op-identifier-prefix))))))) + (cons 'inlambda bracket-pos)) + ((and c-recognize-paren-inexpr-blocks + block-follows + containing-sexp + (eq (char-after containing-sexp) ?\()) (goto-char containing-sexp) (if (or (save-excursion (c-backward-syntactic-ws lim) @@ -10089,7 +10138,7 @@ comment at the start of cc-engine.el for more info." (and c-special-brace-lists (c-looking-at-special-brace-list))) nil - (cons 'inexpr-statement (point)))) + (cons 'inexpr-statement (point))))) res)))) @@ -10115,6 +10164,18 @@ comment at the start of cc-engine.el for more info." paren-state) containing-sexp))))) +(defun c-looking-at-c++-lambda-capture-list () + ;; Return non-nil if we're at the opening "[" of the capture list of a C++ + ;; lambda function, nil otherwise. + (and + (eq (char-after) ?\[) + (not (eq (char-before) ?\[)) + (not (eq (char-after (1+ (point))) ?\[)) + (save-excursion + (or (eq (c-backward-token-2 1) 1) + (looking-at c-pre-lambda-tokens-re))) + (not (c-in-literal)))) + (defun c-at-macro-vsemi-p (&optional pos) ;; Is there a "virtual semicolon" at POS or point? ;; (See cc-defs.el for full details of "virtual semicolons".) diff --git a/lisp/progmodes/cc-fonts.el b/lisp/progmodes/cc-fonts.el index b45686c81b0..ae18d0a9436 100644 --- a/lisp/progmodes/cc-fonts.el +++ b/lisp/progmodes/cc-fonts.el @@ -1242,6 +1242,20 @@ casts and declarations are fontified. Used on level 2 and higher." ((eq type 'c-decl-arg-start) (setq context 'decl c-restricted-<>-arglists nil)) + ;; Inside a C++11 lambda function arglist. + ((and (c-major-mode-is 'c++-mode) + (eq (char-before match-pos) ?\() + (save-excursion + (goto-char match-pos) + (c-backward-token-2) + (and + (c-safe (goto-char (scan-sexps (point) -1))) + (c-looking-at-c++-lambda-capture-list)))) + (setq context 'decl + c-restricted-<>-arglists nil) + (c-put-char-property (1- match-pos) 'c-type + 'c-decl-arg-start)) + ;; Inside an angle bracket arglist. ((or (eq type 'c-<>-arg-sep) (eq (char-before match-pos) ?<)) @@ -1583,6 +1597,90 @@ casts and declarations are fontified. Used on level 2 and higher." (setq raw-id (match-string-no-properties 2))))))))) nil) +(defun c-font-lock-c++-lambda-captures (limit) + ;; Fontify the lambda capture component of C++ lambda declarations. + ;; + ;; This function will be called from font-lock for a region bounded by POINT + ;; and LIMIT, as though it were to identify a keyword for + ;; font-lock-keyword-face. It always returns NIL to inhibit this and + ;; prevent a repeat invocation. See elisp/lispref page "Search-based + ;; Fontification". + (let (mode capture-default id-start id-end declaration sub-begin sub-end) + (while (and (< (point) limit) + (search-forward "[" limit t)) + (when (progn (backward-char) + (prog1 + (c-looking-at-c++-lambda-capture-list) + (forward-char))) + (c-forward-syntactic-ws) + (setq mode (and (memq (char-after) '(?= ?&)) + (char-after))) + ;; Is the first element of the list a bare "=" or "&"? + (when mode + (forward-char) + (c-forward-syntactic-ws) + (if (memq (char-after) '(?, ?\])) + (progn + (setq capture-default mode) + (when (eq (char-after) ?,) + (forward-char) + (c-forward-syntactic-ws))) + (c-backward-token-2))) + + ;; Go round the following loop once per captured item. + (while (and (not (eq (char-after) ?\])) + (< (point) limit)) + (if (eq (char-after) ?&) + (progn (setq mode ?&) + (forward-char) + (c-forward-syntactic-ws)) + (setq mode ?=)) + (if (c-on-identifier) + (progn + (setq id-start (point)) + (forward-char) + (c-end-of-current-token) + (setq id-end (point)) + (c-forward-syntactic-ws) + + (setq declaration (eq (char-after) ?=)) + (when declaration + (forward-char) ; over "=" + (c-forward-syntactic-ws) + (setq sub-begin (point))) + (if (or (and (< (point) limit) + (c-syntactic-re-search-forward "," limit t t)) + (and (c-go-up-list-forward nil limit) + (eq (char-before) ?\]))) + (backward-char) + (goto-char limit)) + (when declaration + (save-excursion + (setq sub-end (point)) + (goto-char sub-begin) + (c-font-lock-c++-lambda-captures sub-end))) + + (c-put-font-lock-face id-start id-end + (cond + (declaration + 'font-lock-variable-name-face) + ((and capture-default + (eq mode capture-default)) + 'font-lock-warning-face) + ((eq mode ?=) font-lock-constant-face) + (t 'font-lock-variable-name-face)))) + (c-syntactic-re-search-forward "," limit 'bound t)) + + (c-forward-syntactic-ws) + (when (eq (char-after) ?,) + (forward-char) + (c-forward-syntactic-ws))) + + (setq capture-default nil) + (forward-char)))) ; over the terminating "]". + nil) + + (c-lang-defconst c-simple-decl-matchers "Simple font lock matchers for types and declarations. These are used on level 2 only and so aren't combined with `c-complex-decl-matchers'." @@ -1700,6 +1798,9 @@ on level 2 only and so aren't combined with `c-complex-decl-matchers'." ,@(when (c-lang-const c-recognize-<>-arglists) `(c-font-lock-<>-arglists)) + ,@(when (c-major-mode-is 'c++-mode) + `(c-font-lock-c++-lambda-captures)) + ;; The first two rules here mostly find occurrences that ;; `c-font-lock-declarations' has found already, but not ;; declarations containing blocks in the type (see note below). diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el index ec894f619a8..934186da7bd 100644 --- a/lisp/progmodes/cc-langs.el +++ b/lisp/progmodes/cc-langs.el @@ -474,6 +474,7 @@ so that all identifiers are recognized as words.") ;; The value here may be a list of functions or a single function. t nil c++ '(c-extend-region-for-CPP +; c-before-after-change-extend-region-for-lambda-capture ; doesn't seem needed. c-before-change-check-raw-strings c-before-change-check-<>-operators c-depropertize-CPP @@ -517,6 +518,7 @@ parameters \(point-min) and \(point-max).") c-change-expand-fl-region) c++ '(c-depropertize-new-text c-extend-font-lock-region-for-macros +; c-before-after-change-extend-region-for-lambda-capture ; doens't seem needed. c-before-after-change-digit-quote c-after-change-re-mark-raw-strings c-neutralize-syntax-in-and-mark-CPP @@ -1360,6 +1362,25 @@ operators." t '(";" "{" "}")) (c-lang-defvar c-pre-start-tokens (c-lang-const c-pre-start-tokens)) +(c-lang-defconst c-pre-lambda-tokens + "List of tokens which may precede a lambda declaration. +In C++ this is something like \"[a,b] (foo, bar) -> int { ... };\". +Currently (2016-08) only used in C++ mode." + t (c--set-difference + (c--delete-duplicates + (append (c-lang-const c-operator-list) + (c-lang-const c-other-op-syntax-tokens))) + (append + '("#" "%:" "??=" "##" "%:%:" "??=??=" "::" "." "->" + "]" "<:" ":>" "??(" "??)" "??-" "new" "delete" + ")" ".*" "->*" "??'" "??!" "??!??!" "??!=" "??'=") + '("<%" "%>" "<:" ":>" "%:" "%:%:" "#" "##" "::" "...")) + :test #'string-equal)) + +(c-lang-defconst c-pre-lambda-tokens-re + ;; Regexp matching any token in the list `c-pre-lambda-tokens'. + t (regexp-opt (c-lang-const c-pre-lambda-tokens))) +(c-lang-defvar c-pre-lambda-tokens-re (c-lang-const c-pre-lambda-tokens-re)) ;;; Syntactic whitespace. @@ -2284,7 +2305,8 @@ contain type identifiers." (c c++) '(;; GCC extension. "__attribute__" ;; MSVC extension. - "__declspec")) + "__declspec") + c++ (append (c-lang-const c-paren-nontype-kwds) '("noexcept"))) (c-lang-defconst c-paren-nontype-key t (c-make-keywords-re t (c-lang-const c-paren-nontype-kwds))) diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el index 07476013354..f630b053edc 100644 --- a/lisp/progmodes/cc-mode.el +++ b/lisp/progmodes/cc-mode.el @@ -1142,6 +1142,38 @@ Note that the style variables are always made local to the buffer." (goto-char try-end) (setq num-begin (point))))) +;; The following doesn't seem needed at the moment (2016-08-15). +;; (defun c-before-after-change-extend-region-for-lambda-capture +;; (_beg _end &optional _old-len) +;; ;; In C++ Mode, extend the region (c-new-BEG c-new-END) to cover any lambda +;; ;; function capture lists we happen to be inside. This function is expected +;; ;; to be called both as a before-change and after change function. +;; ;; +;; ;; Note that these things _might_ be nested, with a capture list looking +;; ;; like: +;; ;; +;; ;; [ ...., &foo = [..](){...}(..), ... ] +;; ;; +;; ;; . What a wonderful language is C++. ;-) +;; (c-save-buffer-state (paren-state pos) +;; (goto-char c-new-BEG) +;; (setq paren-state (c-parse-state)) +;; (while (setq pos (c-pull-open-brace paren-state)) +;; (goto-char pos) +;; (when (c-looking-at-c++-lambda-capture-list) +;; (setq c-new-BEG (min c-new-BEG pos)) +;; (if (c-go-list-forward) +;; (setq c-new-END (max c-new-END (point)))))) + +;; (goto-char c-new-END) +;; (setq paren-state (c-parse-state)) +;; (while (setq pos (c-pull-open-brace paren-state)) +;; (goto-char pos) +;; (when (c-looking-at-c++-lambda-capture-list) +;; (setq c-new-BEG (min c-new-BEG pos)) +;; (if (c-go-list-forward) +;; (setq c-new-END (max c-new-END (point)))))))) + (defun c-before-change (beg end) ;; Function to be put on `before-change-functions'. Primarily, this calls ;; the language dependent `c-get-state-before-change-functions'. It is @@ -1329,12 +1361,24 @@ Note that the style variables are always made local to the buffer." ;; lock context (etc.) fontification. (let ((lit-start (c-literal-start)) (new-pos pos) + capture-opener bod-lim bo-decl) (goto-char (c-point 'bol new-pos)) (when lit-start ; Comment or string. (goto-char lit-start)) (setq bod-lim (c-determine-limit 500)) + ;; In C++ Mode, first check if we are within a (possibly nested) lambda + ;; form capture list. + (when (c-major-mode-is 'c++-mode) + (let ((paren-state (c-parse-state)) + opener) + (save-excursion + (while (setq opener (c-pull-open-brace paren-state)) + (goto-char opener) + (if (c-looking-at-c++-lambda-capture-list) + (setq capture-opener (point))))))) + (while ;; Go to a less nested declaration each time round this loop. (and @@ -1361,6 +1405,8 @@ Note that the style variables are always made local to the buffer." c-<-as-paren-syntax))))) (not (bobp))) (backward-char)) ; back over (, [, <. + (when (and capture-opener (< capture-opener new-pos)) + (setq new-pos capture-opener)) (and (/= new-pos pos) new-pos))) (defun c-change-expand-fl-region (_beg _end _old-len) -- 2.11.4.GIT