@atlaskit/editor-plugin-autocomplete 9.1.0 → 9.2.1

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.
@@ -90,7 +90,7 @@ var printLegend = function printLegend(verbose) {
90
90
  // eslint-disable-next-line no-console
91
91
  console.log('%cPlanes%c [CTC:init] loads · [CTC:signal] semantic/network · [CTC:model] primes/exact · [CTC:model-cost] prefill/decode · [CTC:readiness] deadline progress', CTC_STYLES.section, CTC_STYLES.body);
92
92
  // eslint-disable-next-line no-console
93
- console.log('%cInspect%c __atlCtcDebug__.enable("verbose") · .disable() · .session() (L1 boosts, narrow to a family with .session("poll"))', CTC_STYLES.section, CTC_STYLES.body);
93
+ console.log('%cInspect%c __atlCtcDebug__.enable("verbose") · .disable() · .harvest() (session inline code) · .session() (L1 boosts) — both narrow to a prefix, e.g. .session("poll")', CTC_STYLES.section, CTC_STYLES.body);
94
94
  // eslint-disable-next-line no-console
95
95
  console.groupEnd();
96
96
  };
@@ -139,13 +139,24 @@ export var isAutocompleteDebugVerbose = function isAutocompleteDebugVerbose() {
139
139
  };
140
140
 
141
141
  /**
142
- * Hang the L1 session-boost snapshot off the console API.
142
+ * Hang the harvest snapshot off the console API.
143
143
  *
144
144
  * Unlike the log helpers this is available whether or not debug is enabled:
145
145
  * inspecting state on demand is not logging, and asking someone to turn on
146
146
  * logging and retype to find out what the session already holds defeats the
147
147
  * point of being able to ask.
148
148
  */
149
+ export var registerCtcHarvestInspector = function registerCtcHarvestInspector(inspect) {
150
+ var api = getDebugApi();
151
+ if (api) {
152
+ api.harvest = inspect;
153
+ }
154
+ };
155
+
156
+ /**
157
+ * Hang the L1 session-boost snapshot off the console API, on the same terms as
158
+ * `registerCtcHarvestInspector`: available whether or not logging is on.
159
+ */
149
160
  export var registerCtcSessionInspector = function registerCtcSessionInspector(inspect) {
150
161
  var api = getDebugApi();
151
162
  if (api) {
@@ -0,0 +1,572 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
3
+ import _toArray from "@babel/runtime/helpers/toArray";
4
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
5
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
6
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
7
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
8
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
9
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
10
+ /**
11
+ * Inline-code harvester — the Class B candidate source.
12
+ *
13
+ * Class A terms (Atlassian, Confluence) already carry tenant frequencies, a word
14
+ * vector and canonical token ids, so they compete inside the scored pool. Class B
15
+ * terms — `ml-studio` and friends — exist only in the session and cannot be
16
+ * suggested at all today: `incrementSessionFreq` only touches trie nodes that
17
+ * already exist, so an unknown session word is silently discarded.
18
+ *
19
+ * Two sources carry the signal. Assistant replies arrive as markdown strings and
20
+ * keep their backticks; the live document has had its backticks consumed by the
21
+ * text-formatting input rule, so its spans are found through the `code` mark
22
+ * instead. Human chat messages arrive as ADF and are flattened to bare text, so
23
+ * they contribute no spans — which is why no author check is needed here.
24
+ *
25
+ * A harvested surface cannot be scored: there is no frequency to rank it, no
26
+ * vector to place it in context and no canonical token ids to price it under the
27
+ * model. Being marked as code is the whole of the evidence, so what stands in for
28
+ * a score is where the surface is allowed to speak — only on a prefix no
29
+ * vocabulary reached, and only once the scored path has finished with it.
30
+ *
31
+ * Sightings are still counted, per source and by different rules, but they do
32
+ * not admit a surface. They order the set under eviction, and they pick between
33
+ * two surfaces that complete the same typed prefix — see `HarvestedTerm` and
34
+ * `findHarvestedCompletion`.
35
+ *
36
+ * What the session is holding is visible at any time from the console:
37
+ * `__atlCtcDebug__.harvest()`, or `__atlCtcDebug__.harvest('ml-s')` to ask what
38
+ * a prefix would be offered — see `inspectInlineCodeHarvest`.
39
+ */
40
+
41
+ import { CTC_STYLES, ctcTag, isAutocompleteDebugEnabled, isAutocompleteDebugVerbose, registerCtcHarvestInspector } from './debug-mode';
42
+ import { lookupVocabularySource } from './text-predictor';
43
+
44
+ /** Longer than this is a command or a path, not a term anyone wants completed. */
45
+ var MAX_IDENTIFIER_LENGTH = 50;
46
+
47
+ /**
48
+ * Longer than the predictor's DISPLAY_MIN_PREFIX_LENGTH of 3.
49
+ *
50
+ * The scored path can afford three characters because it has a posterior and a
51
+ * winner margin to answer for the fourth. This path has neither, so it asks the
52
+ * user to commit further before it will guess.
53
+ */
54
+ var MIN_TYPED_PREFIX_LENGTH = 4;
55
+
56
+ /** Mirrors the predictor's MIN_SUGGESTION_LENGTH. */
57
+ var MIN_GHOST_LENGTH = 3;
58
+
59
+ /**
60
+ * Sightings needed before a surface may be offered, counting both sources.
61
+ *
62
+ * One, because asking for two reads the replies this exists for as no evidence
63
+ * at all: a reply answering "what are our service names" names each one exactly
64
+ * once, and ten terms mentioned once each is the normal shape of the answer, not
65
+ * a weak signal. What keeps noise out is not a count — see `isDisplayEligible`.
66
+ */
67
+ var MIN_OCCURRENCES = 1;
68
+
69
+ /**
70
+ * Cap on the harvested set.
71
+ *
72
+ * Not a memory bound — surfaces are capped at 50 characters. It is there so a
73
+ * long session's early terms cannot sit in the set competing with what is being
74
+ * discussed now, and so the display path's scan stays bounded. Provisional:
75
+ * size it off the funnel numbers from a branch deploy rather than this guess.
76
+ */
77
+ var MAX_HARVESTED_TERMS = 200;
78
+
79
+ /**
80
+ * A surface shorter than a qualifying prefix plus a qualifying ghost can never
81
+ * be shown. Recorded rather than filtered, so the funnel shows how much of the
82
+ * harvest is unusable for that reason alone.
83
+ */
84
+ var DISPLAYABLE_MIN_LENGTH = MIN_TYPED_PREFIX_LENGTH + MIN_GHOST_LENGTH;
85
+ var FENCED_REGION_REGEX = /```(?:[\0-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF])*?```/g;
86
+ var INLINE_CODE_SPAN_REGEX = /`((?:[\0-\t\x0B-_a-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)`/g;
87
+ var WHITESPACE_REGEX = /[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]/;
88
+ var STARTS_WITH_LETTER_REGEX = /^(?:[A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0560-\u0588\u05D0-\u05EA\u05EF-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u0870-\u0887\u0889-\u088F\u08A0-\u08C9\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C5C\u0C5D\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDC-\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D04-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E86-\u0E8A\u0E8C-\u0EA3\u0EA5\u0EA7-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u1711\u171F-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1878\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4C\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C8A\u1C90-\u1CBA\u1CBD-\u1CBF\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u1CFA\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312F\u3131-\u318E\u31A0-\u31BF\u31F0-\u31FF\u3400-\u4DBF\u4E00-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7DC\uA7F1-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA8FE\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB69\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF2D-\uDF40\uDF42-\uDF49\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF]|\uD801[\uDC00-\uDC9D\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDD70-\uDD7A\uDD7C-\uDD8A\uDD8C-\uDD92\uDD94\uDD95\uDD97-\uDDA1\uDDA3-\uDDB1\uDDB3-\uDDB9\uDDBB\uDDBC\uDDC0-\uDDF3\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67\uDF80-\uDF85\uDF87-\uDFB0\uDFB2-\uDFBA]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD40-\uDD59\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE35\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2\uDD00-\uDD23\uDD4A-\uDD65\uDD6F-\uDD85\uDE80-\uDEA9\uDEB0\uDEB1\uDEC2-\uDEC7\uDF00-\uDF1C\uDF27\uDF30-\uDF45\uDF70-\uDF81\uDFB0-\uDFC4\uDFE0-\uDFF6]|\uD804[\uDC03-\uDC37\uDC71\uDC72\uDC75\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD44\uDD47\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE3F\uDE40\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61\uDF80-\uDF89\uDF8B\uDF8E\uDF90-\uDFB5\uDFB7\uDFD1\uDFD3]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC5F-\uDC61\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDEB8\uDF00-\uDF1A\uDF40-\uDF46]|\uD806[\uDC00-\uDC2B\uDCA0-\uDCDF\uDCFF-\uDD06\uDD09\uDD0C-\uDD13\uDD15\uDD16\uDD18-\uDD2F\uDD3F\uDD41\uDDA0-\uDDA7\uDDAA-\uDDD0\uDDE1\uDDE3\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE89\uDE9D\uDEB0-\uDEF8\uDFC0-\uDFE0]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46\uDD60-\uDD65\uDD67\uDD68\uDD6A-\uDD89\uDD98\uDDB0-\uDDDB\uDEE0-\uDEF2\uDF02\uDF04-\uDF10\uDF12-\uDF33\uDFB0]|\uD808[\uDC00-\uDF99]|\uD809[\uDC80-\uDD43]|\uD80B[\uDF90-\uDFF0]|[\uD80C\uD80E\uD80F\uD81C-\uD822\uD840-\uD868\uD86A-\uD86D\uD86F-\uD872\uD874-\uD879\uD880-\uD883\uD885-\uD88C][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2F\uDC41-\uDC46\uDC60-\uDFFF]|\uD810[\uDC00-\uDFFA]|\uD811[\uDC00-\uDE46]|\uD818[\uDD00-\uDD1D]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE70-\uDEBE\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDD40-\uDD6C\uDE40-\uDE7F\uDEA0-\uDEB8\uDEBB-\uDED3\uDF00-\uDF4A\uDF50\uDF93-\uDF9F\uDFE0\uDFE1\uDFE3\uDFF2\uDFF3]|\uD823[\uDC00-\uDCD5\uDCFF-\uDD1E\uDD80-\uDDF2]|\uD82B[\uDFF0-\uDFF3\uDFF5-\uDFFB\uDFFD\uDFFE]|\uD82C[\uDC00-\uDD22\uDD32\uDD50-\uDD52\uDD55\uDD64-\uDD67\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD837[\uDF00-\uDF1E\uDF25-\uDF2A]|\uD838[\uDC30-\uDC6D\uDD00-\uDD2C\uDD37-\uDD3D\uDD4E\uDE90-\uDEAD\uDEC0-\uDEEB]|\uD839[\uDCD0-\uDCEB\uDDD0-\uDDED\uDDF0\uDEC0-\uDEDE\uDEE0-\uDEE2\uDEE4\uDEE5\uDEE7-\uDEED\uDEF0-\uDEF4\uDEFE\uDEFF\uDFE0-\uDFE6\uDFE8-\uDFEB\uDFED\uDFEE\uDFF0-\uDFFE]|\uD83A[\uDC00-\uDCC4\uDD00-\uDD43\uDD4B]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDEDF\uDF00-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEAD\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0\uDFF0-\uDFFF]|\uD87B[\uDC00-\uDE5D]|\uD87E[\uDC00-\uDE1D]|\uD884[\uDC00-\uDF4A\uDF50-\uDFFF]|\uD88D[\uDC00-\uDC79])/;
89
+
90
+ /** Why this surface was chosen over the others that completed the prefix. */
91
+
92
+ var emptyFunnel = function emptyFunnel() {
93
+ return {
94
+ accepted: 0,
95
+ rejectedInFence: 0,
96
+ rejectedKnownL2: 0,
97
+ rejectedKnownL3: 0,
98
+ rejectedNonAlphaStart: 0,
99
+ rejectedTooLong: 0,
100
+ rejectedWhitespace: 0,
101
+ spansFound: 0
102
+ };
103
+ };
104
+
105
+ // Session-scoped, and only correct because the plugin resets it on destroy: the
106
+ // map holds user and assistant content, so surviving an unmount would leak one
107
+ // conversation's terms into the next.
108
+ var harvested = new Map();
109
+ // Two funnels because the two sources are read on different schedules. Ingested
110
+ // text is seen once, so its funnel accumulates; the document is re-walked on
111
+ // every word boundary, so its funnel is replaced to stay a description of the
112
+ // document rather than of how long the session has run.
113
+ var contextFunnel = emptyFunnel();
114
+ var documentFunnel = emptyFunnel();
115
+ var evictedTerms = 0;
116
+ var lastLoggedSignature = '';
117
+ var collectFencedRanges = function collectFencedRanges(text) {
118
+ var ranges = [];
119
+ var _iterator = _createForOfIteratorHelper(text.matchAll(FENCED_REGION_REGEX)),
120
+ _step;
121
+ try {
122
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
123
+ var match = _step.value;
124
+ if (match.index !== undefined) {
125
+ ranges.push([match.index, match.index + match[0].length]);
126
+ }
127
+ }
128
+ } catch (err) {
129
+ _iterator.e(err);
130
+ } finally {
131
+ _iterator.f();
132
+ }
133
+ return ranges;
134
+ };
135
+ var isInsideFence = function isInsideFence(ranges, index) {
136
+ return ranges.some(function (_ref) {
137
+ var _ref2 = _slicedToArray(_ref, 2),
138
+ start = _ref2[0],
139
+ end = _ref2[1];
140
+ return index >= start && index < end;
141
+ });
142
+ };
143
+
144
+ /** The cleaned surface if it survives intake, or null. Records the rejection. */
145
+ var admitCandidate = function admitCandidate(raw, funnel) {
146
+ // Markdown permits one space of padding inside the ticks, so trim before
147
+ // testing for the internal whitespace that separates a term from a command.
148
+ var candidate = raw.trim();
149
+ if (candidate.length === 0 || WHITESPACE_REGEX.test(candidate)) {
150
+ funnel.rejectedWhitespace++;
151
+ return null;
152
+ }
153
+ if (candidate.length > MAX_IDENTIFIER_LENGTH) {
154
+ funnel.rejectedTooLong++;
155
+ return null;
156
+ }
157
+ // Flags (`-d`, `--open-url`) match every other rule and are worthless to
158
+ // complete, so the leading character has to be a letter.
159
+ if (!STARTS_WITH_LETTER_REGEX.test(candidate)) {
160
+ funnel.rejectedNonAlphaStart++;
161
+ return null;
162
+ }
163
+ // A surface the scored path can already serve does not belong here. Harvesting
164
+ // it would put a candidate with no frequencies, no vector and no canonical
165
+ // token ids up against one that has all three.
166
+ var known = lookupVocabularySource(candidate);
167
+ if (known === 'l2') {
168
+ funnel.rejectedKnownL2++;
169
+ return null;
170
+ }
171
+ if (known === 'l3') {
172
+ funnel.rejectedKnownL3++;
173
+ return null;
174
+ }
175
+ return candidate;
176
+ };
177
+
178
+ /** Total mentions behind a surface, across both sources. */
179
+ var sightingsOf = function sightingsOf(term) {
180
+ return term.documentSpans + term.replyOccurrences;
181
+ };
182
+
183
+ /**
184
+ * Order two surfaces that are competing for the same typed prefix, best first.
185
+ *
186
+ * Sightings lead, because the number of times the session named a thing is the
187
+ * only quantity this module has that says anything about which one is being
188
+ * discussed. The alphabetical tie-break is arbitrary and chosen for being
189
+ * arbitrary in a stable way: on a tie there is nothing to prefer, and a rule
190
+ * that always resolves the same way means the same prefix produces the same
191
+ * ghost every time rather than one that follows harvest order.
192
+ *
193
+ * Numeric-aware so `model@v2` sorts before `model@v10` — versioned identifiers
194
+ * are common enough in this set to be worth not reading as strings.
195
+ */
196
+ var byCollapseOrder = function byCollapseOrder(a, b) {
197
+ return sightingsOf(b) - sightingsOf(a) || a.surface.localeCompare(b.surface, 'en', {
198
+ numeric: true,
199
+ sensitivity: 'base'
200
+ });
201
+ };
202
+
203
+ /**
204
+ * Drop the weakest terms until the set is within its cap.
205
+ *
206
+ * Least evidence first, oldest sighting to break the tie. FIFO is the local
207
+ * precedent (MAX_INGESTED_CONTEXT_TEXTS in the plugin) but it is the wrong
208
+ * policy here: the earliest-harvested term may be the one the conversation is
209
+ * actually about, and the sighting count is the only ranking this module has.
210
+ */
211
+ var evictWeakestTerms = function evictWeakestTerms() {
212
+ if (harvested.size <= MAX_HARVESTED_TERMS) {
213
+ return;
214
+ }
215
+ var ordered = Array.from(harvested.entries()).sort(function (_ref3, _ref4) {
216
+ var _ref5 = _slicedToArray(_ref3, 2),
217
+ a = _ref5[1];
218
+ var _ref6 = _slicedToArray(_ref4, 2),
219
+ b = _ref6[1];
220
+ return sightingsOf(a) - sightingsOf(b) || a.lastSeenAt - b.lastSeenAt;
221
+ });
222
+ var _iterator2 = _createForOfIteratorHelper(ordered.slice(0, harvested.size - MAX_HARVESTED_TERMS)),
223
+ _step2;
224
+ try {
225
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
226
+ var _step2$value = _slicedToArray(_step2.value, 1),
227
+ key = _step2$value[0];
228
+ harvested.delete(key);
229
+ evictedTerms++;
230
+ }
231
+ } catch (err) {
232
+ _iterator2.e(err);
233
+ } finally {
234
+ _iterator2.f();
235
+ }
236
+ };
237
+
238
+ /** Count each admitted surface once per occurrence within a single pass. */
239
+ var tallySpans = function tallySpans(surfaces) {
240
+ var counts = new Map();
241
+ var _iterator3 = _createForOfIteratorHelper(surfaces),
242
+ _step3;
243
+ try {
244
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
245
+ var surface = _step3.value;
246
+ var key = surface.toLowerCase();
247
+ var existing = counts.get(key);
248
+ if (existing) {
249
+ existing.count++;
250
+ } else {
251
+ counts.set(key, {
252
+ count: 1,
253
+ surface: surface
254
+ });
255
+ }
256
+ }
257
+ } catch (err) {
258
+ _iterator3.e(err);
259
+ } finally {
260
+ _iterator3.f();
261
+ }
262
+ return counts;
263
+ };
264
+
265
+ /**
266
+ * Harvest single-backtick spans from a markdown-ish string. Fenced regions are
267
+ * skipped: they hold commands and file paths, and their contents are whitespace
268
+ * separated anyway.
269
+ */
270
+ export var harvestInlineCodeFromText = function harvestInlineCodeFromText(text) {
271
+ if (!text) {
272
+ return;
273
+ }
274
+ var fencedRanges = collectFencedRanges(text);
275
+ var admitted = [];
276
+ var _iterator4 = _createForOfIteratorHelper(text.matchAll(INLINE_CODE_SPAN_REGEX)),
277
+ _step4;
278
+ try {
279
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
280
+ var match = _step4.value;
281
+ if (match.index === undefined) {
282
+ continue;
283
+ }
284
+ contextFunnel.spansFound++;
285
+ if (isInsideFence(fencedRanges, match.index)) {
286
+ contextFunnel.rejectedInFence++;
287
+ continue;
288
+ }
289
+ var surface = admitCandidate(match[1], contextFunnel);
290
+ if (surface) {
291
+ admitted.push(surface);
292
+ }
293
+ }
294
+ } catch (err) {
295
+ _iterator4.e(err);
296
+ } finally {
297
+ _iterator4.f();
298
+ }
299
+ var now = performance.now();
300
+ var _iterator5 = _createForOfIteratorHelper(tallySpans(admitted)),
301
+ _step5;
302
+ try {
303
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
304
+ var _step5$value = _slicedToArray(_step5.value, 2),
305
+ key = _step5$value[0],
306
+ _step5$value$ = _step5$value[1],
307
+ count = _step5$value$.count,
308
+ _surface = _step5$value$.surface;
309
+ var existing = harvested.get(key);
310
+ if (existing) {
311
+ existing.replyOccurrences += count;
312
+ existing.lastSeenAt = now;
313
+ continue;
314
+ }
315
+ harvested.set(key, {
316
+ displayable: _surface.length >= DISPLAYABLE_MIN_LENGTH,
317
+ documentSpans: 0,
318
+ lastSeenAt: now,
319
+ replyOccurrences: count,
320
+ surface: _surface
321
+ });
322
+ contextFunnel.accepted++;
323
+ }
324
+ } catch (err) {
325
+ _iterator5.e(err);
326
+ } finally {
327
+ _iterator5.f();
328
+ }
329
+ evictWeakestTerms();
330
+ };
331
+
332
+ /**
333
+ * Harvest code-marked text from the live document. The backtick input rule fires
334
+ * on the closing tick and removes both delimiters, so a finished span is only
335
+ * findable through its mark. Code blocks are skipped for the same reason fenced
336
+ * regions are.
337
+ *
338
+ * The whole document is re-read, and what it finds replaces the previous
339
+ * document counts rather than adding to them.
340
+ */
341
+ export var harvestInlineCodeFromDoc = function harvestInlineCodeFromDoc(doc) {
342
+ documentFunnel = emptyFunnel();
343
+ var admitted = [];
344
+ doc.descendants(function (node) {
345
+ if (node.type.name === 'codeBlock') {
346
+ return false;
347
+ }
348
+ if (node.isText && node.text && node.marks.some(function (mark) {
349
+ return mark.type.name === 'code';
350
+ })) {
351
+ documentFunnel.spansFound++;
352
+ var surface = admitCandidate(node.text, documentFunnel);
353
+ if (surface) {
354
+ admitted.push(surface);
355
+ }
356
+ }
357
+ return true;
358
+ });
359
+ var spans = tallySpans(admitted);
360
+ // Surfaces the document no longer holds lose their document evidence. They
361
+ // stay in the set on whatever reply evidence they have.
362
+ var _iterator6 = _createForOfIteratorHelper(harvested),
363
+ _step6;
364
+ try {
365
+ for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
366
+ var _step6$value = _slicedToArray(_step6.value, 2),
367
+ key = _step6$value[0],
368
+ term = _step6$value[1];
369
+ if (!spans.has(key)) {
370
+ term.documentSpans = 0;
371
+ }
372
+ }
373
+ } catch (err) {
374
+ _iterator6.e(err);
375
+ } finally {
376
+ _iterator6.f();
377
+ }
378
+ var now = performance.now();
379
+ var _iterator7 = _createForOfIteratorHelper(spans),
380
+ _step7;
381
+ try {
382
+ for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) {
383
+ var _step7$value = _slicedToArray(_step7.value, 2),
384
+ _key = _step7$value[0],
385
+ _step7$value$ = _step7$value[1],
386
+ count = _step7$value$.count,
387
+ surface = _step7$value$.surface;
388
+ var existing = harvested.get(_key);
389
+ if (existing) {
390
+ existing.documentSpans = count;
391
+ existing.lastSeenAt = now;
392
+ continue;
393
+ }
394
+ harvested.set(_key, {
395
+ displayable: surface.length >= DISPLAYABLE_MIN_LENGTH,
396
+ documentSpans: count,
397
+ lastSeenAt: now,
398
+ replyOccurrences: 0,
399
+ surface: surface
400
+ });
401
+ documentFunnel.accepted++;
402
+ }
403
+ } catch (err) {
404
+ _iterator7.e(err);
405
+ } finally {
406
+ _iterator7.f();
407
+ }
408
+ evictWeakestTerms();
409
+ };
410
+
411
+ /**
412
+ * Whether a surface still has a sighting behind it.
413
+ *
414
+ * Recurrence turned out to be the wrong thing to lean on. What separates a term
415
+ * worth offering from a word in prose is that someone marked it as code — the
416
+ * author with a code mark, or the model with backticks — and that is already
417
+ * true of everything in this set. The rules that keep noise out are elsewhere:
418
+ * the surface must be absent from L2 and L3, unique among harvested terms for
419
+ * the typed prefix, and on a prefix the scored path has no claim to.
420
+ *
421
+ * The case this still rejects is a withdrawn sighting: a document span the
422
+ * author deleted drops back to zero, and a term with no reply mention behind it
423
+ * stops being offered.
424
+ */
425
+ var isDisplayEligible = function isDisplayEligible(term) {
426
+ return term.documentSpans + term.replyOccurrences >= MIN_OCCURRENCES;
427
+ };
428
+
429
+ /**
430
+ * Every harvested surface that could be shown for `typedPrefix`, best first.
431
+ *
432
+ * A surface whose remaining tail is too short to render is not a rival, it is a
433
+ * surface the user has finished typing. Neither is one whose sighting has been
434
+ * withdrawn. Both used to be counted as ambiguity and used to silence the path.
435
+ */
436
+ var collectCandidates = function collectCandidates(typedPrefix) {
437
+ var needle = typedPrefix.toLowerCase();
438
+ var candidates = [];
439
+ var _iterator8 = _createForOfIteratorHelper(harvested.values()),
440
+ _step8;
441
+ try {
442
+ for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) {
443
+ var term = _step8.value;
444
+ if (!term.surface.toLowerCase().startsWith(needle)) {
445
+ continue;
446
+ }
447
+ if (term.surface.length - typedPrefix.length < MIN_GHOST_LENGTH) {
448
+ continue;
449
+ }
450
+ if (!isDisplayEligible(term)) {
451
+ continue;
452
+ }
453
+ candidates.push(term);
454
+ }
455
+ } catch (err) {
456
+ _iterator8.e(err);
457
+ } finally {
458
+ _iterator8.f();
459
+ }
460
+ return candidates.sort(byCollapseOrder);
461
+ };
462
+
463
+ /**
464
+ * The harvested surface to offer for `typedPrefix`, or null.
465
+ *
466
+ * Rivals are collapsed rather than treated as a reason to stay quiet. Silence
467
+ * was the original rule, on the grounds that two session surfaces sharing a
468
+ * prefix have nothing to separate them, and it was wrong for the case this path
469
+ * exists to serve: a reply that answers "what are our services" names ten of
470
+ * them, several share a stem, and abstaining meant the harvester went quiet on
471
+ * exactly the reply it was built for. Being wrong here costs one keystroke —
472
+ * the ghost is ignored and typing continues — and staying silent costs the
473
+ * feature.
474
+ *
475
+ * `collapsedBy` records which rule settled it so a surprising ghost can be
476
+ * explained after the fact rather than guessed at.
477
+ */
478
+ export var findHarvestedCompletion = function findHarvestedCompletion(typedPrefix) {
479
+ if (typedPrefix.length < MIN_TYPED_PREFIX_LENGTH) {
480
+ return null;
481
+ }
482
+ var _collectCandidates = collectCandidates(typedPrefix),
483
+ _collectCandidates2 = _toArray(_collectCandidates),
484
+ winner = _collectCandidates2[0],
485
+ runnerUp = _collectCandidates2[1],
486
+ rest = _arrayLikeToArray(_collectCandidates2).slice(2);
487
+ if (!winner) {
488
+ return null;
489
+ }
490
+ return {
491
+ collapsedBy: !runnerUp ? 'sole-match' : sightingsOf(winner) > sightingsOf(runnerUp) ? 'sightings' : 'alphabetical',
492
+ documentSpans: winner.documentSpans,
493
+ ghostText: winner.surface.slice(typedPrefix.length),
494
+ replyOccurrences: winner.replyOccurrences,
495
+ rivalSurfaces: (runnerUp ? [runnerUp].concat(_toConsumableArray(rest)) : []).map(function (term) {
496
+ return term.surface;
497
+ }),
498
+ surface: winner.surface,
499
+ typedPrefixLength: typedPrefix.length
500
+ };
501
+ };
502
+ /**
503
+ * Read the session's harvest, optionally asking what `typedPrefix` would get.
504
+ *
505
+ * Installed as `__atlCtcDebug__.harvest()` and returned rather than logged, so
506
+ * the console renders it as an inspectable object and a caller can assert on it.
507
+ */
508
+ export var inspectInlineCodeHarvest = function inspectInlineCodeHarvest(typedPrefix) {
509
+ return _objectSpread(_objectSpread({
510
+ cap: MAX_HARVESTED_TERMS,
511
+ evicted: evictedTerms,
512
+ funnels: {
513
+ context: _objectSpread({}, contextFunnel),
514
+ liveDocument: _objectSpread({}, documentFunnel)
515
+ }
516
+ }, typedPrefix === undefined ? {} : {
517
+ match: findHarvestedCompletion(typedPrefix)
518
+ }), {}, {
519
+ terms: Array.from(harvested.values()).sort(byCollapseOrder).map(function (term) {
520
+ return {
521
+ documentSpans: term.documentSpans,
522
+ longEnough: term.displayable,
523
+ offerable: isDisplayEligible(term),
524
+ replyOccurrences: term.replyOccurrences,
525
+ sightings: sightingsOf(term),
526
+ surface: term.surface
527
+ };
528
+ })
529
+ });
530
+ };
531
+
532
+ // At module scope so the console API answers before the first keystroke, which
533
+ // is when someone reaching for it usually asks.
534
+ registerCtcHarvestInspector(inspectInlineCodeHarvest);
535
+ export var resetInlineCodeHarvest = function resetInlineCodeHarvest() {
536
+ harvested.clear();
537
+ contextFunnel = emptyFunnel();
538
+ documentFunnel = emptyFunnel();
539
+ evictedTerms = 0;
540
+ lastLoggedSignature = '';
541
+ };
542
+
543
+ /**
544
+ * Print the funnel when it has moved since the last print.
545
+ *
546
+ * Deliberately reports zero-span and all-rejected passes too. Logging only on a
547
+ * successful harvest makes "the replies held no inline code", "every span was
548
+ * filtered out" and "this never ran" indistinguishable, which are the three
549
+ * things worth telling apart.
550
+ */
551
+ export var logInlineCodeHarvest = function logInlineCodeHarvest(trigger) {
552
+ if (!isAutocompleteDebugEnabled()) {
553
+ return;
554
+ }
555
+ var snapshot = inspectInlineCodeHarvest();
556
+ var offerable = snapshot.terms.filter(function (term) {
557
+ return term.offerable && term.longEnough;
558
+ });
559
+ var spansFound = contextFunnel.spansFound + documentFunnel.spansFound;
560
+ var signature = "".concat(spansFound, ":").concat(snapshot.terms.length, ":").concat(offerable.length);
561
+ if (signature === lastLoggedSignature) {
562
+ return;
563
+ }
564
+ lastLoggedSignature = signature;
565
+ ctcTag('harvest', "".concat(trigger, " \xB7 ").concat(spansFound, " inline spans \u2192 ").concat(snapshot.terms.length, " kept, ").concat(offerable.length, " offerable").concat(evictedTerms > 0 ? ", ".concat(evictedTerms, " evicted") : '', " \xB7 __atlCtcDebug__.harvest() to inspect"), CTC_STYLES.lm);
566
+ if (isAutocompleteDebugVerbose()) {
567
+ // eslint-disable-next-line no-console
568
+ console.table(snapshot.funnels);
569
+ // eslint-disable-next-line no-console
570
+ console.table(snapshot.terms);
571
+ }
572
+ };