@codemirror/autocomplete 6.2.0 → 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 6.3.0 (2022-09-22)
2
+
3
+ ### New features
4
+
5
+ Close bracket configuration now supports a `stringPrefixes` property that can be used to allow autoclosing of prefixed strings.
6
+
1
7
  ## 6.2.0 (2022-09-13)
2
8
 
3
9
  ### New features
package/dist/index.cjs CHANGED
@@ -509,7 +509,7 @@ class CompletionTooltip {
509
509
  let sel = this.dom.querySelector("[aria-selected]");
510
510
  if (!sel || !this.info)
511
511
  return null;
512
- let win = this.dom.ownerDocument.defaultView;
512
+ let win = this.dom.ownerDocument.defaultView || window;
513
513
  let listRect = this.dom.getBoundingClientRect();
514
514
  let infoRect = this.info.getBoundingClientRect();
515
515
  let selRect = sel.getBoundingClientRect();
@@ -1091,6 +1091,7 @@ const baseTheme = view.EditorView.baseTheme({
1091
1091
  verticalAlign: "text-top",
1092
1092
  width: 0,
1093
1093
  height: "1.15em",
1094
+ display: "inline-block",
1094
1095
  margin: "0 -0.7px -.7em",
1095
1096
  borderLeft: "1.4px dotted #888"
1096
1097
  },
@@ -1474,7 +1475,8 @@ const completeAnyWord = context => {
1474
1475
 
1475
1476
  const defaults = {
1476
1477
  brackets: ["(", "[", "{", "'", '"'],
1477
- before: ")]}:;>"
1478
+ before: ")]}:;>",
1479
+ stringPrefixes: []
1478
1480
  };
1479
1481
  const closeBracketEffect = state.StateEffect.define({
1480
1482
  map(value, mapping) {
@@ -1590,7 +1592,7 @@ function insertBracket(state$1, bracket) {
1590
1592
  for (let tok of tokens) {
1591
1593
  let closed = closing(state.codePointAt(tok, 0));
1592
1594
  if (bracket == tok)
1593
- return closed == tok ? handleSame(state$1, tok, tokens.indexOf(tok + tok + tok) > -1)
1595
+ return closed == tok ? handleSame(state$1, tok, tokens.indexOf(tok + tok + tok) > -1, conf)
1594
1596
  : handleOpen(state$1, tok, closed, conf.before || defaults.before);
1595
1597
  if (bracket == closed && closedBracketAt(state$1, state$1.selection.main.from))
1596
1598
  return handleClose(state$1, tok, closed);
@@ -1645,13 +1647,14 @@ function handleClose(state$1, _open, close) {
1645
1647
  }
1646
1648
  // Handles cases where the open and close token are the same, and
1647
1649
  // possibly triple quotes (as in `"""abc"""`-style quoting).
1648
- function handleSame(state$1, token, allowTriple) {
1650
+ function handleSame(state$1, token, allowTriple, config) {
1651
+ let stringPrefixes = config.stringPrefixes || defaults.stringPrefixes;
1649
1652
  let dont = null, changes = state$1.changeByRange(range => {
1650
1653
  if (!range.empty)
1651
1654
  return { changes: [{ insert: token, from: range.from }, { insert: token, from: range.to }],
1652
1655
  effects: closeBracketEffect.of(range.to + token.length),
1653
1656
  range: state.EditorSelection.range(range.anchor + token.length, range.head + token.length) };
1654
- let pos = range.head, next = nextChar(state$1.doc, pos);
1657
+ let pos = range.head, next = nextChar(state$1.doc, pos), start;
1655
1658
  if (next == token) {
1656
1659
  if (nodeStart(state$1, pos)) {
1657
1660
  return { changes: { insert: token + token, from: pos },
@@ -1665,14 +1668,14 @@ function handleSame(state$1, token, allowTriple) {
1665
1668
  }
1666
1669
  }
1667
1670
  else if (allowTriple && state$1.sliceDoc(pos - 2 * token.length, pos) == token + token &&
1668
- nodeStart(state$1, pos - 2 * token.length)) {
1671
+ (start = canStartStringAt(state$1, pos - 2 * token.length, stringPrefixes)) > -1 &&
1672
+ nodeStart(state$1, start)) {
1669
1673
  return { changes: { insert: token + token + token + token, from: pos },
1670
1674
  effects: closeBracketEffect.of(pos + token.length),
1671
1675
  range: state.EditorSelection.cursor(pos + token.length) };
1672
1676
  }
1673
1677
  else if (state$1.charCategorizer(pos)(next) != state.CharCategory.Word) {
1674
- let prev = state$1.sliceDoc(pos - 1, pos);
1675
- if (prev != token && state$1.charCategorizer(pos)(prev) != state.CharCategory.Word && !probablyInString(state$1, pos, token))
1678
+ if (canStartStringAt(state$1, pos, stringPrefixes) > -1 && !probablyInString(state$1, pos, token, stringPrefixes))
1676
1679
  return { changes: { insert: token + token, from: pos },
1677
1680
  effects: closeBracketEffect.of(pos + token.length),
1678
1681
  range: state.EditorSelection.cursor(pos + token.length) };
@@ -1688,12 +1691,15 @@ function nodeStart(state, pos) {
1688
1691
  let tree = language.syntaxTree(state).resolveInner(pos + 1);
1689
1692
  return tree.parent && tree.from == pos;
1690
1693
  }
1691
- function probablyInString(state, pos, quoteToken) {
1694
+ function probablyInString(state, pos, quoteToken, prefixes) {
1692
1695
  let node = language.syntaxTree(state).resolveInner(pos, -1);
1696
+ let maxPrefix = prefixes.reduce((m, p) => Math.max(m, p.length), 0);
1693
1697
  for (let i = 0; i < 5; i++) {
1694
- if (state.sliceDoc(node.from, node.from + quoteToken.length) == quoteToken) {
1698
+ let start = state.sliceDoc(node.from, Math.min(node.to, node.from + quoteToken.length + maxPrefix));
1699
+ let quotePos = start.indexOf(quoteToken);
1700
+ if (!quotePos || quotePos > -1 && prefixes.indexOf(start.slice(0, quotePos)) > -1) {
1695
1701
  let first = node.firstChild;
1696
- while (first && first.from == node.from && first.to - first.from > quoteToken.length) {
1702
+ while (first && first.from == node.from && first.to - first.from > quoteToken.length + quotePos) {
1697
1703
  if (state.sliceDoc(first.to - quoteToken.length, first.to) == quoteToken)
1698
1704
  return false;
1699
1705
  first = first.firstChild;
@@ -1707,6 +1713,17 @@ function probablyInString(state, pos, quoteToken) {
1707
1713
  }
1708
1714
  return false;
1709
1715
  }
1716
+ function canStartStringAt(state$1, pos, prefixes) {
1717
+ let charCat = state$1.charCategorizer(pos);
1718
+ if (charCat(state$1.sliceDoc(pos - 1, pos)) != state.CharCategory.Word)
1719
+ return pos;
1720
+ for (let prefix of prefixes) {
1721
+ let start = pos - prefix.length;
1722
+ if (state$1.sliceDoc(start, pos) == prefix && charCat(state$1.sliceDoc(start - 1, start)) != state.CharCategory.Word)
1723
+ return start;
1724
+ }
1725
+ return -1;
1726
+ }
1710
1727
 
1711
1728
  /**
1712
1729
  Returns an extension that enables autocompletion.
package/dist/index.d.ts CHANGED
@@ -66,7 +66,8 @@ interface CompletionConfig {
66
66
  completion, and should produce a DOM node to show. `position`
67
67
  determines where in the DOM the result appears, relative to
68
68
  other added widgets and the standard content. The default icons
69
- have position 20, the label position 50, and the detail position 70.
69
+ have position 20, the label position 50, and the detail position
70
+ 80.
70
71
  */
71
72
  addToOptions?: {
72
73
  render: (completion: Completion, state: EditorState) => Node | null;
@@ -404,6 +405,11 @@ interface CloseBracketConfig {
404
405
  whitespace. Defaults to `")]}:;>"`.
405
406
  */
406
407
  before?: string;
408
+ /**
409
+ When determining whether a given node may be a string, recognize
410
+ these prefixes before the opening quote.
411
+ */
412
+ stringPrefixes?: string[];
407
413
  }
408
414
  /**
409
415
  Extension to enable bracket-closing behavior. When a closeable
package/dist/index.js CHANGED
@@ -505,7 +505,7 @@ class CompletionTooltip {
505
505
  let sel = this.dom.querySelector("[aria-selected]");
506
506
  if (!sel || !this.info)
507
507
  return null;
508
- let win = this.dom.ownerDocument.defaultView;
508
+ let win = this.dom.ownerDocument.defaultView || window;
509
509
  let listRect = this.dom.getBoundingClientRect();
510
510
  let infoRect = this.info.getBoundingClientRect();
511
511
  let selRect = sel.getBoundingClientRect();
@@ -1087,6 +1087,7 @@ const baseTheme = /*@__PURE__*/EditorView.baseTheme({
1087
1087
  verticalAlign: "text-top",
1088
1088
  width: 0,
1089
1089
  height: "1.15em",
1090
+ display: "inline-block",
1090
1091
  margin: "0 -0.7px -.7em",
1091
1092
  borderLeft: "1.4px dotted #888"
1092
1093
  },
@@ -1470,7 +1471,8 @@ const completeAnyWord = context => {
1470
1471
 
1471
1472
  const defaults = {
1472
1473
  brackets: ["(", "[", "{", "'", '"'],
1473
- before: ")]}:;>"
1474
+ before: ")]}:;>",
1475
+ stringPrefixes: []
1474
1476
  };
1475
1477
  const closeBracketEffect = /*@__PURE__*/StateEffect.define({
1476
1478
  map(value, mapping) {
@@ -1586,7 +1588,7 @@ function insertBracket(state, bracket) {
1586
1588
  for (let tok of tokens) {
1587
1589
  let closed = closing(codePointAt(tok, 0));
1588
1590
  if (bracket == tok)
1589
- return closed == tok ? handleSame(state, tok, tokens.indexOf(tok + tok + tok) > -1)
1591
+ return closed == tok ? handleSame(state, tok, tokens.indexOf(tok + tok + tok) > -1, conf)
1590
1592
  : handleOpen(state, tok, closed, conf.before || defaults.before);
1591
1593
  if (bracket == closed && closedBracketAt(state, state.selection.main.from))
1592
1594
  return handleClose(state, tok, closed);
@@ -1641,13 +1643,14 @@ function handleClose(state, _open, close) {
1641
1643
  }
1642
1644
  // Handles cases where the open and close token are the same, and
1643
1645
  // possibly triple quotes (as in `"""abc"""`-style quoting).
1644
- function handleSame(state, token, allowTriple) {
1646
+ function handleSame(state, token, allowTriple, config) {
1647
+ let stringPrefixes = config.stringPrefixes || defaults.stringPrefixes;
1645
1648
  let dont = null, changes = state.changeByRange(range => {
1646
1649
  if (!range.empty)
1647
1650
  return { changes: [{ insert: token, from: range.from }, { insert: token, from: range.to }],
1648
1651
  effects: closeBracketEffect.of(range.to + token.length),
1649
1652
  range: EditorSelection.range(range.anchor + token.length, range.head + token.length) };
1650
- let pos = range.head, next = nextChar(state.doc, pos);
1653
+ let pos = range.head, next = nextChar(state.doc, pos), start;
1651
1654
  if (next == token) {
1652
1655
  if (nodeStart(state, pos)) {
1653
1656
  return { changes: { insert: token + token, from: pos },
@@ -1661,14 +1664,14 @@ function handleSame(state, token, allowTriple) {
1661
1664
  }
1662
1665
  }
1663
1666
  else if (allowTriple && state.sliceDoc(pos - 2 * token.length, pos) == token + token &&
1664
- nodeStart(state, pos - 2 * token.length)) {
1667
+ (start = canStartStringAt(state, pos - 2 * token.length, stringPrefixes)) > -1 &&
1668
+ nodeStart(state, start)) {
1665
1669
  return { changes: { insert: token + token + token + token, from: pos },
1666
1670
  effects: closeBracketEffect.of(pos + token.length),
1667
1671
  range: EditorSelection.cursor(pos + token.length) };
1668
1672
  }
1669
1673
  else if (state.charCategorizer(pos)(next) != CharCategory.Word) {
1670
- let prev = state.sliceDoc(pos - 1, pos);
1671
- if (prev != token && state.charCategorizer(pos)(prev) != CharCategory.Word && !probablyInString(state, pos, token))
1674
+ if (canStartStringAt(state, pos, stringPrefixes) > -1 && !probablyInString(state, pos, token, stringPrefixes))
1672
1675
  return { changes: { insert: token + token, from: pos },
1673
1676
  effects: closeBracketEffect.of(pos + token.length),
1674
1677
  range: EditorSelection.cursor(pos + token.length) };
@@ -1684,12 +1687,15 @@ function nodeStart(state, pos) {
1684
1687
  let tree = syntaxTree(state).resolveInner(pos + 1);
1685
1688
  return tree.parent && tree.from == pos;
1686
1689
  }
1687
- function probablyInString(state, pos, quoteToken) {
1690
+ function probablyInString(state, pos, quoteToken, prefixes) {
1688
1691
  let node = syntaxTree(state).resolveInner(pos, -1);
1692
+ let maxPrefix = prefixes.reduce((m, p) => Math.max(m, p.length), 0);
1689
1693
  for (let i = 0; i < 5; i++) {
1690
- if (state.sliceDoc(node.from, node.from + quoteToken.length) == quoteToken) {
1694
+ let start = state.sliceDoc(node.from, Math.min(node.to, node.from + quoteToken.length + maxPrefix));
1695
+ let quotePos = start.indexOf(quoteToken);
1696
+ if (!quotePos || quotePos > -1 && prefixes.indexOf(start.slice(0, quotePos)) > -1) {
1691
1697
  let first = node.firstChild;
1692
- while (first && first.from == node.from && first.to - first.from > quoteToken.length) {
1698
+ while (first && first.from == node.from && first.to - first.from > quoteToken.length + quotePos) {
1693
1699
  if (state.sliceDoc(first.to - quoteToken.length, first.to) == quoteToken)
1694
1700
  return false;
1695
1701
  first = first.firstChild;
@@ -1703,6 +1709,17 @@ function probablyInString(state, pos, quoteToken) {
1703
1709
  }
1704
1710
  return false;
1705
1711
  }
1712
+ function canStartStringAt(state, pos, prefixes) {
1713
+ let charCat = state.charCategorizer(pos);
1714
+ if (charCat(state.sliceDoc(pos - 1, pos)) != CharCategory.Word)
1715
+ return pos;
1716
+ for (let prefix of prefixes) {
1717
+ let start = pos - prefix.length;
1718
+ if (state.sliceDoc(start, pos) == prefix && charCat(state.sliceDoc(start - 1, start)) != CharCategory.Word)
1719
+ return start;
1720
+ }
1721
+ return -1;
1722
+ }
1706
1723
 
1707
1724
  /**
1708
1725
  Returns an extension that enables autocompletion.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/autocomplete",
3
- "version": "6.2.0",
3
+ "version": "6.3.0",
4
4
  "description": "Autocompletion for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",