aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPo Lu2023-04-30 08:25:24 +0800
committerPo Lu2023-04-30 08:25:24 +0800
commit4289ed6cffdb5ea758a78037fe385fd7c4e23677 (patch)
treeae58e266b0a4a7b659bb667ff68571a2b649106e
parentfee9efdf29080922698c5c1e102126e0a20915b4 (diff)
parent30892cbd330ace125454a056ca86d85e2a860bf5 (diff)
downloademacs-4289ed6cffdb5ea758a78037fe385fd7c4e23677.tar.gz
emacs-4289ed6cffdb5ea758a78037fe385fd7c4e23677.zip
Merge remote-tracking branch 'origin/master' into feature/android
-rw-r--r--etc/EGLOT-NEWS26
-rw-r--r--lisp/progmodes/eglot.el6
-rw-r--r--lisp/progmodes/elixir-ts-mode.el6
-rw-r--r--src/composite.c16
-rw-r--r--src/composite.h2
-rw-r--r--src/indent.c8
-rw-r--r--src/xdisp.c35
-rw-r--r--test/lisp/progmodes/elixir-ts-mode-resources/indent.erts26
8 files changed, 99 insertions, 26 deletions
diff --git a/etc/EGLOT-NEWS b/etc/EGLOT-NEWS
index 7369b3bf545..27796adef4b 100644
--- a/etc/EGLOT-NEWS
+++ b/etc/EGLOT-NEWS
@@ -18,6 +18,32 @@ to look up issue github#1234, go to
18https://github.com/joaotavora/eglot/issues/1234. 18https://github.com/joaotavora/eglot/issues/1234.
19 19
20 20
21* Changes in Eglot 1.15 (29/4/2023)
22
23** Fix LSP "languageId" detection
24
25Many servers today support multiple languages, meaning they can handle
26more than one file type in the same connection. This relies on the
27client supplying a ':languageId' string. Previously, Eglot calculated
28this string based on an imperfect heuristic and was often wrong. See
29github#1206.
30
31** Fix problems with missing signature documentation (bug#62687)
32
33** Reworked 'eglot-imenu'
34
35Eglot's Imenu backend (used for M-x imenu among other extensions), has
36been reworked. Most newer servers respond to
37'textDocument/documentSymbol' with a vector of 'DocumentSymbol', not
38'SymbolInformation'. It's not worth it trying to make the two formats
39resemble each other. This also lays groundwork supporting a
40forthcoming "breadcrumb" feature of bug#58431.
41
42** New command 'eglot-update'
43
44This allows users to easily update to the latest version of Eglot.
45
46
21* Changes in Eglot 1.14 (3/4/2023) 47* Changes in Eglot 1.14 (3/4/2023)
22 48
23** Faster, more responsive completion 49** Faster, more responsive completion
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index 6ae27a13845..1e0bcd30485 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -2,7 +2,7 @@
2 2
3;; Copyright (C) 2018-2023 Free Software Foundation, Inc. 3;; Copyright (C) 2018-2023 Free Software Foundation, Inc.
4 4
5;; Version: 1.14 5;; Version: 1.15
6;; Author: João Távora <joaotavora@gmail.com> 6;; Author: João Távora <joaotavora@gmail.com>
7;; Maintainer: João Távora <joaotavora@gmail.com> 7;; Maintainer: João Távora <joaotavora@gmail.com>
8;; URL: https://github.com/joaotavora/eglot 8;; URL: https://github.com/joaotavora/eglot
@@ -2237,7 +2237,7 @@ COMMAND is a symbol naming the command."
2237 ((eq (car pr) 'eglot--mode-line-reporter) 2237 ((eq (car pr) 'eglot--mode-line-reporter)
2238 (setcdr (cddr pr) (list msg pcnt)) 2238 (setcdr (cddr pr) (list msg pcnt))
2239 (force-mode-line-update t)) 2239 (force-mode-line-update t))
2240 (pr (progress-reporter-update pr pcnt msg))))) 2240 (pr (eglot--reporter-update pr pcnt msg)))))
2241 (eglot--dbind ((WorkDoneProgress) kind title percentage message) value 2241 (eglot--dbind ((WorkDoneProgress) kind title percentage message) value
2242 (pcase kind 2242 (pcase kind
2243 ("begin" 2243 ("begin"
@@ -3341,7 +3341,7 @@ Returns a list as described in docstring of `imenu--index-alist'."
3341 (save-restriction 3341 (save-restriction
3342 (narrow-to-region beg end) 3342 (narrow-to-region beg end)
3343 (replace-buffer-contents temp))) 3343 (replace-buffer-contents temp)))
3344 (progress-reporter-update reporter (cl-incf done))))))) 3344 (eglot--reporter-update reporter (cl-incf done)))))))
3345 (mapcar (eglot--lambda ((TextEdit) range newText) 3345 (mapcar (eglot--lambda ((TextEdit) range newText)
3346 (cons newText (eglot--range-region range 'markers))) 3346 (cons newText (eglot--range-region range 'markers)))
3347 (reverse edits))) 3347 (reverse edits)))
diff --git a/lisp/progmodes/elixir-ts-mode.el b/lisp/progmodes/elixir-ts-mode.el
index c58854c41c3..7175fe4bff8 100644
--- a/lisp/progmodes/elixir-ts-mode.el
+++ b/lisp/progmodes/elixir-ts-mode.el
@@ -89,7 +89,7 @@
89 (rx bol 89 (rx bol
90 (or "call" "stab_clause" "binary_operator" "list" "tuple" "map" "pair" 90 (or "call" "stab_clause" "binary_operator" "list" "tuple" "map" "pair"
91 "sigil" "string" "atom" "alias" "arguments" "identifier" 91 "sigil" "string" "atom" "alias" "arguments" "identifier"
92 "boolean" "quoted_content") 92 "boolean" "quoted_content" "bitstring")
93 eol)) 93 eol))
94 94
95(defconst elixir-ts--test-definition-keywords 95(defconst elixir-ts--test-definition-keywords
@@ -231,6 +231,7 @@
231 ((node-is "^]$") ,'elixir-ts--parent-expression-start 0) 231 ((node-is "^]$") ,'elixir-ts--parent-expression-start 0)
232 ((node-is "^}$") ,'elixir-ts--parent-expression-start 0) 232 ((node-is "^}$") ,'elixir-ts--parent-expression-start 0)
233 ((node-is "^)$") ,'elixir-ts--parent-expression-start 0) 233 ((node-is "^)$") ,'elixir-ts--parent-expression-start 0)
234 ((node-is "^>>$") ,'elixir-ts--parent-expression-start 0)
234 ((node-is "^else_block$") grand-parent 0) 235 ((node-is "^else_block$") grand-parent 0)
235 ((node-is "^catch_block$") grand-parent 0) 236 ((node-is "^catch_block$") grand-parent 0)
236 ((node-is "^rescue_block$") grand-parent 0) 237 ((node-is "^rescue_block$") grand-parent 0)
@@ -250,12 +251,12 @@
250 ,'elixir-ts--argument-indent-anchor 251 ,'elixir-ts--argument-indent-anchor
251 ,'elixir-ts--argument-indent-offset) 252 ,'elixir-ts--argument-indent-offset)
252 ((parent-is "^pair$") parent ,offset) 253 ((parent-is "^pair$") parent ,offset)
254 ((parent-is "^bitstring$") parent ,offset)
253 ((parent-is "^map_content$") parent-bol 0) 255 ((parent-is "^map_content$") parent-bol 0)
254 ((parent-is "^map$") ,'elixir-ts--parent-expression-start ,offset) 256 ((parent-is "^map$") ,'elixir-ts--parent-expression-start ,offset)
255 ((node-is "^stab_clause$") parent-bol ,offset) 257 ((node-is "^stab_clause$") parent-bol ,offset)
256 ((query ,elixir-ts--capture-operator-parent) grand-parent 0) 258 ((query ,elixir-ts--capture-operator-parent) grand-parent 0)
257 ((node-is "^when$") parent 0) 259 ((node-is "^when$") parent 0)
258 ((node-is "^keywords$") parent-bol ,offset)
259 ((parent-is "^body$") 260 ((parent-is "^body$")
260 (lambda (node parent _) 261 (lambda (node parent _)
261 (save-excursion 262 (save-excursion
@@ -270,6 +271,7 @@
270 ,'elixir-ts--argument-indent-anchor 271 ,'elixir-ts--argument-indent-anchor
271 ,'elixir-ts--argument-indent-offset) 272 ,'elixir-ts--argument-indent-offset)
272 ;; Handle incomplete maps when parent is ERROR. 273 ;; Handle incomplete maps when parent is ERROR.
274 ((node-is "^keywords$") parent-bol ,offset)
273 ((n-p-gp "^binary_operator$" "ERROR" nil) parent-bol 0) 275 ((n-p-gp "^binary_operator$" "ERROR" nil) parent-bol 0)
274 ;; When there is an ERROR, just indent to prev-line. 276 ;; When there is an ERROR, just indent to prev-line.
275 ((parent-is "ERROR") prev-line ,offset) 277 ((parent-is "ERROR") prev-line ,offset)
diff --git a/src/composite.c b/src/composite.c
index 164eeb39598..34f369d167a 100644
--- a/src/composite.c
+++ b/src/composite.c
@@ -1040,7 +1040,9 @@ inhibit_auto_composition (void)
1040 composition closest to CHARPOS is found, set cmp_it->stop_pos to 1040 composition closest to CHARPOS is found, set cmp_it->stop_pos to
1041 the last character of the composition. STRING, if non-nil, is 1041 the last character of the composition. STRING, if non-nil, is
1042 the string (as opposed to a buffer) whose characters should be 1042 the string (as opposed to a buffer) whose characters should be
1043 tested for being composable. 1043 tested for being composable. INCLUDE_STATIC non-zero means
1044 consider both static and automatic compositions; if zero, look
1045 only for potential automatic compositions.
1044 1046
1045 If no composition is found, set cmp_it->ch to -2. If a static 1047 If no composition is found, set cmp_it->ch to -2. If a static
1046 composition is found, set cmp_it->ch to -1. Otherwise, set 1048 composition is found, set cmp_it->ch to -1. Otherwise, set
@@ -1050,7 +1052,7 @@ inhibit_auto_composition (void)
1050void 1052void
1051composition_compute_stop_pos (struct composition_it *cmp_it, ptrdiff_t charpos, 1053composition_compute_stop_pos (struct composition_it *cmp_it, ptrdiff_t charpos,
1052 ptrdiff_t bytepos, ptrdiff_t endpos, 1054 ptrdiff_t bytepos, ptrdiff_t endpos,
1053 Lisp_Object string) 1055 Lisp_Object string, bool include_static)
1054{ 1056{
1055 ptrdiff_t start, end; 1057 ptrdiff_t start, end;
1056 int c; 1058 int c;
@@ -1084,8 +1086,10 @@ composition_compute_stop_pos (struct composition_it *cmp_it, ptrdiff_t charpos,
1084 cmp_it->stop_pos = endpos; 1086 cmp_it->stop_pos = endpos;
1085 if (charpos == endpos) 1087 if (charpos == endpos)
1086 return; 1088 return;
1089 /* Look for static compositions. */
1087 /* FIXME: Bidi is not yet handled well in static composition. */ 1090 /* FIXME: Bidi is not yet handled well in static composition. */
1088 if (charpos < endpos 1091 if (include_static
1092 && charpos < endpos
1089 && find_composition (charpos, endpos, &start, &end, &prop, string) 1093 && find_composition (charpos, endpos, &start, &end, &prop, string)
1090 && start >= charpos 1094 && start >= charpos
1091 && composition_valid_p (start, end, prop)) 1095 && composition_valid_p (start, end, prop))
@@ -1106,6 +1110,7 @@ composition_compute_stop_pos (struct composition_it *cmp_it, ptrdiff_t charpos,
1106 bytepos = string_char_to_byte (string, charpos); 1110 bytepos = string_char_to_byte (string, charpos);
1107 } 1111 }
1108 1112
1113 /* Look for automatic compositions. */
1109 start = charpos; 1114 start = charpos;
1110 if (charpos < endpos) 1115 if (charpos < endpos)
1111 { 1116 {
@@ -1285,7 +1290,8 @@ composition_reseat_it (struct composition_it *cmp_it, ptrdiff_t charpos,
1285{ 1290{
1286 if (cmp_it->ch == -2) 1291 if (cmp_it->ch == -2)
1287 { 1292 {
1288 composition_compute_stop_pos (cmp_it, charpos, bytepos, endpos, string); 1293 composition_compute_stop_pos (cmp_it, charpos, bytepos, endpos, string,
1294 true);
1289 if (cmp_it->ch == -2 || cmp_it->stop_pos != charpos) 1295 if (cmp_it->ch == -2 || cmp_it->stop_pos != charpos)
1290 /* The current position is not composed. */ 1296 /* The current position is not composed. */
1291 return 0; 1297 return 0;
@@ -1424,7 +1430,7 @@ composition_reseat_it (struct composition_it *cmp_it, ptrdiff_t charpos,
1424 } 1430 }
1425 if (cmp_it->reversed_p) 1431 if (cmp_it->reversed_p)
1426 endpos = -1; 1432 endpos = -1;
1427 composition_compute_stop_pos (cmp_it, charpos, bytepos, endpos, string); 1433 composition_compute_stop_pos (cmp_it, charpos, bytepos, endpos, string, true);
1428 return 0; 1434 return 0;
1429} 1435}
1430 1436
diff --git a/src/composite.h b/src/composite.h
index e81465d90cc..0f791c1ea62 100644
--- a/src/composite.h
+++ b/src/composite.h
@@ -348,7 +348,7 @@ extern bool find_automatic_composition (ptrdiff_t, ptrdiff_t, ptrdiff_t,
348 348
349extern void composition_compute_stop_pos (struct composition_it *, 349extern void composition_compute_stop_pos (struct composition_it *,
350 ptrdiff_t, ptrdiff_t, ptrdiff_t, 350 ptrdiff_t, ptrdiff_t, ptrdiff_t,
351 Lisp_Object); 351 Lisp_Object, bool);
352extern bool composition_reseat_it (struct composition_it *, ptrdiff_t, 352extern bool composition_reseat_it (struct composition_it *, ptrdiff_t,
353 ptrdiff_t, ptrdiff_t, struct window *, 353 ptrdiff_t, ptrdiff_t, struct window *,
354 signed char, struct face *, Lisp_Object); 354 signed char, struct face *, Lisp_Object);
diff --git a/src/indent.c b/src/indent.c
index 08d2bf5ea28..16285ce84e2 100644
--- a/src/indent.c
+++ b/src/indent.c
@@ -616,7 +616,7 @@ scan_for_column (ptrdiff_t *endpos, EMACS_INT *goalcol,
616 616
617 memset (&cmp_it, 0, sizeof cmp_it); 617 memset (&cmp_it, 0, sizeof cmp_it);
618 cmp_it.id = -1; 618 cmp_it.id = -1;
619 composition_compute_stop_pos (&cmp_it, scan, scan_byte, end, Qnil); 619 composition_compute_stop_pos (&cmp_it, scan, scan_byte, end, Qnil, true);
620 620
621 /* Scan forward to the target position. */ 621 /* Scan forward to the target position. */
622 while (scan < end) 622 while (scan < end)
@@ -681,7 +681,7 @@ scan_for_column (ptrdiff_t *endpos, EMACS_INT *goalcol,
681 { 681 {
682 cmp_it.id = -1; 682 cmp_it.id = -1;
683 composition_compute_stop_pos (&cmp_it, scan, scan_byte, end, 683 composition_compute_stop_pos (&cmp_it, scan, scan_byte, end,
684 Qnil); 684 Qnil, true);
685 } 685 }
686 else 686 else
687 cmp_it.from = cmp_it.to; 687 cmp_it.from = cmp_it.to;
@@ -1290,7 +1290,7 @@ compute_motion (ptrdiff_t from, ptrdiff_t frombyte, EMACS_INT fromvpos,
1290 prev_tab_offset = tab_offset; 1290 prev_tab_offset = tab_offset;
1291 memset (&cmp_it, 0, sizeof cmp_it); 1291 memset (&cmp_it, 0, sizeof cmp_it);
1292 cmp_it.id = -1; 1292 cmp_it.id = -1;
1293 composition_compute_stop_pos (&cmp_it, pos, pos_byte, to, Qnil); 1293 composition_compute_stop_pos (&cmp_it, pos, pos_byte, to, Qnil, true);
1294 1294
1295 unsigned short int quit_count = 0; 1295 unsigned short int quit_count = 0;
1296 1296
@@ -1600,7 +1600,7 @@ compute_motion (ptrdiff_t from, ptrdiff_t frombyte, EMACS_INT fromvpos,
1600 { 1600 {
1601 cmp_it.id = -1; 1601 cmp_it.id = -1;
1602 composition_compute_stop_pos (&cmp_it, pos, pos_byte, to, 1602 composition_compute_stop_pos (&cmp_it, pos, pos_byte, to,
1603 Qnil); 1603 Qnil, true);
1604 } 1604 }
1605 else 1605 else
1606 cmp_it.from = cmp_it.to; 1606 cmp_it.from = cmp_it.to;
diff --git a/src/xdisp.c b/src/xdisp.c
index 046d1f78452..96fb040baa4 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -4056,7 +4056,7 @@ compute_stop_pos (struct it *it)
4056{ 4056{
4057 register INTERVAL iv, next_iv; 4057 register INTERVAL iv, next_iv;
4058 Lisp_Object object, limit, position; 4058 Lisp_Object object, limit, position;
4059 ptrdiff_t charpos, bytepos; 4059 ptrdiff_t charpos, bytepos, cmp_limit_pos = -1;
4060 4060
4061 if (STRINGP (it->string)) 4061 if (STRINGP (it->string))
4062 { 4062 {
@@ -4126,7 +4126,10 @@ compute_stop_pos (struct it *it)
4126 } 4126 }
4127 } 4127 }
4128 if (found) 4128 if (found)
4129 pos--; 4129 {
4130 pos--;
4131 cmp_limit_pos = pos;
4132 }
4130 else if (it->stop_charpos < endpos) 4133 else if (it->stop_charpos < endpos)
4131 pos = it->stop_charpos; 4134 pos = it->stop_charpos;
4132 else 4135 else
@@ -4184,14 +4187,25 @@ compute_stop_pos (struct it *it)
4184 } 4187 }
4185 } 4188 }
4186 4189
4187 if (it->cmp_it.id < 0) 4190 if (it->cmp_it.id < 0
4191 && (STRINGP (it->string)
4192 || ((!it->bidi_p || it->bidi_it.scan_dir >= 0)
4193 && it->cmp_it.stop_pos <= IT_CHARPOS (*it))))
4188 { 4194 {
4189 ptrdiff_t stoppos = it->end_charpos; 4195 ptrdiff_t stoppos = it->end_charpos;
4190 4196
4197 /* If we found, above, a buffer position that cannot be part of
4198 an automatic composition, limit the search of composable
4199 characters to that position. */
4191 if (it->bidi_p && it->bidi_it.scan_dir < 0) 4200 if (it->bidi_p && it->bidi_it.scan_dir < 0)
4192 stoppos = -1; 4201 stoppos = -1;
4202 else if (cmp_limit_pos > 0)
4203 stoppos = cmp_limit_pos;
4204 /* Force composition_compute_stop_pos avoid the costly search
4205 for static compositions, since those were already found by
4206 looking at text properties, above. */
4193 composition_compute_stop_pos (&it->cmp_it, charpos, bytepos, 4207 composition_compute_stop_pos (&it->cmp_it, charpos, bytepos,
4194 stoppos, it->string); 4208 stoppos, it->string, false);
4195 } 4209 }
4196 4210
4197 eassert (STRINGP (it->string) 4211 eassert (STRINGP (it->string)
@@ -7716,7 +7730,7 @@ reseat_to_string (struct it *it, const char *s, Lisp_Object string,
7716 if (endpos > it->end_charpos) 7730 if (endpos > it->end_charpos)
7717 endpos = it->end_charpos; 7731 endpos = it->end_charpos;
7718 composition_compute_stop_pos (&it->cmp_it, charpos, -1, endpos, 7732 composition_compute_stop_pos (&it->cmp_it, charpos, -1, endpos,
7719 it->string); 7733 it->string, true);
7720 } 7734 }
7721 CHECK_IT (it); 7735 CHECK_IT (it);
7722} 7736}
@@ -8404,7 +8418,7 @@ set_iterator_to_next (struct it *it, bool reseat_p)
8404 where to stop. */ 8418 where to stop. */
8405 stop = -1; 8419 stop = -1;
8406 composition_compute_stop_pos (&it->cmp_it, IT_CHARPOS (*it), 8420 composition_compute_stop_pos (&it->cmp_it, IT_CHARPOS (*it),
8407 IT_BYTEPOS (*it), stop, Qnil); 8421 IT_BYTEPOS (*it), stop, Qnil, true);
8408 } 8422 }
8409 } 8423 }
8410 else 8424 else
@@ -8435,7 +8449,8 @@ set_iterator_to_next (struct it *it, bool reseat_p)
8435 if (it->bidi_it.scan_dir < 0) 8449 if (it->bidi_it.scan_dir < 0)
8436 stop = -1; 8450 stop = -1;
8437 composition_compute_stop_pos (&it->cmp_it, IT_CHARPOS (*it), 8451 composition_compute_stop_pos (&it->cmp_it, IT_CHARPOS (*it),
8438 IT_BYTEPOS (*it), stop, Qnil); 8452 IT_BYTEPOS (*it), stop, Qnil,
8453 true);
8439 } 8454 }
8440 } 8455 }
8441 eassert (IT_BYTEPOS (*it) == CHAR_TO_BYTE (IT_CHARPOS (*it))); 8456 eassert (IT_BYTEPOS (*it) == CHAR_TO_BYTE (IT_CHARPOS (*it)));
@@ -8591,7 +8606,7 @@ set_iterator_to_next (struct it *it, bool reseat_p)
8591 composition_compute_stop_pos (&it->cmp_it, 8606 composition_compute_stop_pos (&it->cmp_it,
8592 IT_STRING_CHARPOS (*it), 8607 IT_STRING_CHARPOS (*it),
8593 IT_STRING_BYTEPOS (*it), stop, 8608 IT_STRING_BYTEPOS (*it), stop,
8594 it->string); 8609 it->string, true);
8595 } 8610 }
8596 } 8611 }
8597 else 8612 else
@@ -8628,7 +8643,7 @@ set_iterator_to_next (struct it *it, bool reseat_p)
8628 composition_compute_stop_pos (&it->cmp_it, 8643 composition_compute_stop_pos (&it->cmp_it,
8629 IT_STRING_CHARPOS (*it), 8644 IT_STRING_CHARPOS (*it),
8630 IT_STRING_BYTEPOS (*it), stop, 8645 IT_STRING_BYTEPOS (*it), stop,
8631 it->string); 8646 it->string, true);
8632 } 8647 }
8633 } 8648 }
8634 } 8649 }
@@ -8879,7 +8894,7 @@ get_visually_first_element (struct it *it)
8879 if (it->bidi_it.scan_dir < 0) 8894 if (it->bidi_it.scan_dir < 0)
8880 stop = -1; 8895 stop = -1;
8881 composition_compute_stop_pos (&it->cmp_it, charpos, bytepos, stop, 8896 composition_compute_stop_pos (&it->cmp_it, charpos, bytepos, stop,
8882 it->string); 8897 it->string, true);
8883 } 8898 }
8884} 8899}
8885 8900
diff --git a/test/lisp/progmodes/elixir-ts-mode-resources/indent.erts b/test/lisp/progmodes/elixir-ts-mode-resources/indent.erts
index 9ad604e5198..1f855d3c977 100644
--- a/test/lisp/progmodes/elixir-ts-mode-resources/indent.erts
+++ b/test/lisp/progmodes/elixir-ts-mode-resources/indent.erts
@@ -79,6 +79,18 @@ def foo() do
79end 79end
80=-=-= 80=-=-=
81 81
82Name: Bitstring mulitline
83
84=-=
85<<12, 22,
8622, 32
87 >>
88=-=
89<<12, 22,
90 22, 32
91>>
92=-=-=
93
82Name: Block assignments 94Name: Block assignments
83 95
84=-= 96=-=
@@ -181,7 +193,19 @@ tuple = {
181} 193}
182=-=-= 194=-=-=
183 195
184Name: Spec and method 196Name: Call with keywords
197
198=-=
199def foo() do
200 bar(:one,
201 :two,
202 one: 1,
203 two: 2
204 )
205end
206=-=-=
207
208Name: Call with @spec
185 209
186=-= 210=-=
187@spec foobar( 211@spec foobar(