@atlaskit/editor-plugin-autocomplete 4.0.0 → 4.0.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @atlaskit/editor-plugin-autocomplete
2
2
 
3
+ ## 4.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`560ddb7a914f6`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/560ddb7a914f6) -
8
+ [ux] Fixes a bug where autocomplete suggestions were shown when the cursor was positioned in the
9
+ middle of an existing word. Suggestions are now suppressed unless the cursor is at the trailing
10
+ edge of a token.
11
+ - Updated dependencies
12
+
3
13
  ## 4.0.0
4
14
 
5
15
  ### Major Changes
@@ -188,6 +188,38 @@ var buildSlowLaneText = function buildSlowLaneText(docText, context) {
188
188
  lines.push("reply ".concat(nextReplyNumber, ": ").concat(docText));
189
189
  return lines.join('\n');
190
190
  };
191
+ var createMidWordCharacterRegex = function createMidWordCharacterRegex() {
192
+ try {
193
+ // string-built regex avoids TS1501 under the package's ES5 build target
194
+ return new RegExp('[\\p{L}\\p{N}_]', 'u');
195
+ } catch (_unused) {
196
+ return null;
197
+ }
198
+ };
199
+ var midWordCharacterRegex = createMidWordCharacterRegex();
200
+ var isAsciiWordCharacter = function isAsciiWordCharacter(char) {
201
+ if (char === '_') {
202
+ return true;
203
+ }
204
+ var charCode = char.charCodeAt(0);
205
+ return charCode >= 48 && charCode <= 57 || charCode >= 65 && charCode <= 90 || charCode >= 97 && charCode <= 122;
206
+ };
207
+ var isMidWordCharacter = function isMidWordCharacter(char) {
208
+ if (midWordCharacterRegex) {
209
+ return midWordCharacterRegex.test(char);
210
+ }
211
+ if (isAsciiWordCharacter(char)) {
212
+ return true;
213
+ }
214
+ return char.toLocaleLowerCase() !== char.toLocaleUpperCase();
215
+ };
216
+ var getLeadingTextCharacter = function getLeadingTextCharacter(text) {
217
+ var firstCodePoint = text === null || text === void 0 ? void 0 : text.codePointAt(0);
218
+ if (firstCodePoint === undefined) {
219
+ return undefined;
220
+ }
221
+ return String.fromCodePoint(firstCodePoint);
222
+ };
191
223
  var getTypedLengthForPrediction = function getTypedLengthForPrediction(textBefore) {
192
224
  var _textBefore$match$, _textBefore$match;
193
225
  if ((0, _slowLaneClient.isWordBoundary)(textBefore)) {
@@ -440,6 +472,19 @@ var createAutocompletePlugin = exports.createAutocompletePlugin = function creat
440
472
  if (!selection.empty) {
441
473
  return;
442
474
  }
475
+
476
+ // Suppress suggestions when the cursor is mid-word — only offer
477
+ // completions when the cursor is at the trailing edge of a token.
478
+ // nodeAfter correctly handles inline atoms (mentions, emojis) where
479
+ // parentOffset and textContent indices diverge.
480
+ var nodeAfter = selection.$from.nodeAfter;
481
+ var charAfterCursor = nodeAfter !== null && nodeAfter !== void 0 && nodeAfter.isText ? getLeadingTextCharacter(nodeAfter.text) : undefined;
482
+ // Only suppress for Unicode letters, digits, and underscore — hyphens,
483
+ // apostrophes, and similar punctuation are valid left-edge boundaries
484
+ // and should not block suggestions (e.g. cursor before '-' in compound-word).
485
+ if (charAfterCursor && isMidWordCharacter(charAfterCursor)) {
486
+ return;
487
+ }
443
488
  var textBefore = getTextBeforeCursor(state);
444
489
 
445
490
  // Suppress re-showing the same suggestion the user just dismissed.
@@ -176,6 +176,38 @@ const buildSlowLaneText = (docText, context) => {
176
176
  lines.push(`reply ${nextReplyNumber}: ${docText}`);
177
177
  return lines.join('\n');
178
178
  };
179
+ const createMidWordCharacterRegex = () => {
180
+ try {
181
+ // string-built regex avoids TS1501 under the package's ES5 build target
182
+ return new RegExp('[\\p{L}\\p{N}_]', 'u');
183
+ } catch {
184
+ return null;
185
+ }
186
+ };
187
+ const midWordCharacterRegex = createMidWordCharacterRegex();
188
+ const isAsciiWordCharacter = char => {
189
+ if (char === '_') {
190
+ return true;
191
+ }
192
+ const charCode = char.charCodeAt(0);
193
+ return charCode >= 48 && charCode <= 57 || charCode >= 65 && charCode <= 90 || charCode >= 97 && charCode <= 122;
194
+ };
195
+ const isMidWordCharacter = char => {
196
+ if (midWordCharacterRegex) {
197
+ return midWordCharacterRegex.test(char);
198
+ }
199
+ if (isAsciiWordCharacter(char)) {
200
+ return true;
201
+ }
202
+ return char.toLocaleLowerCase() !== char.toLocaleUpperCase();
203
+ };
204
+ const getLeadingTextCharacter = text => {
205
+ const firstCodePoint = text === null || text === void 0 ? void 0 : text.codePointAt(0);
206
+ if (firstCodePoint === undefined) {
207
+ return undefined;
208
+ }
209
+ return String.fromCodePoint(firstCodePoint);
210
+ };
179
211
  const getTypedLengthForPrediction = textBefore => {
180
212
  var _textBefore$match$, _textBefore$match;
181
213
  if (isWordBoundary(textBefore)) {
@@ -424,6 +456,19 @@ export const createAutocompletePlugin = (options, api) => {
424
456
  if (!selection.empty) {
425
457
  return;
426
458
  }
459
+
460
+ // Suppress suggestions when the cursor is mid-word — only offer
461
+ // completions when the cursor is at the trailing edge of a token.
462
+ // nodeAfter correctly handles inline atoms (mentions, emojis) where
463
+ // parentOffset and textContent indices diverge.
464
+ const nodeAfter = selection.$from.nodeAfter;
465
+ const charAfterCursor = nodeAfter !== null && nodeAfter !== void 0 && nodeAfter.isText ? getLeadingTextCharacter(nodeAfter.text) : undefined;
466
+ // Only suppress for Unicode letters, digits, and underscore — hyphens,
467
+ // apostrophes, and similar punctuation are valid left-edge boundaries
468
+ // and should not block suggestions (e.g. cursor before '-' in compound-word).
469
+ if (charAfterCursor && isMidWordCharacter(charAfterCursor)) {
470
+ return;
471
+ }
427
472
  const textBefore = getTextBeforeCursor(state);
428
473
 
429
474
  // Suppress re-showing the same suggestion the user just dismissed.
@@ -181,6 +181,38 @@ var buildSlowLaneText = function buildSlowLaneText(docText, context) {
181
181
  lines.push("reply ".concat(nextReplyNumber, ": ").concat(docText));
182
182
  return lines.join('\n');
183
183
  };
184
+ var createMidWordCharacterRegex = function createMidWordCharacterRegex() {
185
+ try {
186
+ // string-built regex avoids TS1501 under the package's ES5 build target
187
+ return new RegExp('[\\p{L}\\p{N}_]', 'u');
188
+ } catch (_unused) {
189
+ return null;
190
+ }
191
+ };
192
+ var midWordCharacterRegex = createMidWordCharacterRegex();
193
+ var isAsciiWordCharacter = function isAsciiWordCharacter(char) {
194
+ if (char === '_') {
195
+ return true;
196
+ }
197
+ var charCode = char.charCodeAt(0);
198
+ return charCode >= 48 && charCode <= 57 || charCode >= 65 && charCode <= 90 || charCode >= 97 && charCode <= 122;
199
+ };
200
+ var isMidWordCharacter = function isMidWordCharacter(char) {
201
+ if (midWordCharacterRegex) {
202
+ return midWordCharacterRegex.test(char);
203
+ }
204
+ if (isAsciiWordCharacter(char)) {
205
+ return true;
206
+ }
207
+ return char.toLocaleLowerCase() !== char.toLocaleUpperCase();
208
+ };
209
+ var getLeadingTextCharacter = function getLeadingTextCharacter(text) {
210
+ var firstCodePoint = text === null || text === void 0 ? void 0 : text.codePointAt(0);
211
+ if (firstCodePoint === undefined) {
212
+ return undefined;
213
+ }
214
+ return String.fromCodePoint(firstCodePoint);
215
+ };
184
216
  var getTypedLengthForPrediction = function getTypedLengthForPrediction(textBefore) {
185
217
  var _textBefore$match$, _textBefore$match;
186
218
  if (isWordBoundary(textBefore)) {
@@ -433,6 +465,19 @@ export var createAutocompletePlugin = function createAutocompletePlugin(options,
433
465
  if (!selection.empty) {
434
466
  return;
435
467
  }
468
+
469
+ // Suppress suggestions when the cursor is mid-word — only offer
470
+ // completions when the cursor is at the trailing edge of a token.
471
+ // nodeAfter correctly handles inline atoms (mentions, emojis) where
472
+ // parentOffset and textContent indices diverge.
473
+ var nodeAfter = selection.$from.nodeAfter;
474
+ var charAfterCursor = nodeAfter !== null && nodeAfter !== void 0 && nodeAfter.isText ? getLeadingTextCharacter(nodeAfter.text) : undefined;
475
+ // Only suppress for Unicode letters, digits, and underscore — hyphens,
476
+ // apostrophes, and similar punctuation are valid left-edge boundaries
477
+ // and should not block suggestions (e.g. cursor before '-' in compound-word).
478
+ if (charAfterCursor && isMidWordCharacter(charAfterCursor)) {
479
+ return;
480
+ }
436
481
  var textBefore = getTextBeforeCursor(state);
437
482
 
438
483
  // Suppress re-showing the same suggestion the user just dismissed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-autocomplete",
3
- "version": "4.0.0",
3
+ "version": "4.0.1",
4
4
  "description": "Client-side text autocomplete plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -27,7 +27,7 @@
27
27
  "wink-nlp": "^2.4.0"
28
28
  },
29
29
  "peerDependencies": {
30
- "@atlaskit/editor-common": "^116.0.0",
30
+ "@atlaskit/editor-common": "^116.4.0",
31
31
  "@atlaskit/editor-plugin-analytics": "^12.0.0",
32
32
  "react": "^18.2.0"
33
33
  },
@@ -243,6 +243,52 @@ const buildSlowLaneText = (docText: string, context?: AutocompleteContext): stri
243
243
  return lines.join('\n');
244
244
  };
245
245
 
246
+ const createMidWordCharacterRegex = (): RegExp | null => {
247
+ try {
248
+ // string-built regex avoids TS1501 under the package's ES5 build target
249
+ return new RegExp('[\\p{L}\\p{N}_]', 'u');
250
+ } catch {
251
+ return null;
252
+ }
253
+ };
254
+
255
+ const midWordCharacterRegex = createMidWordCharacterRegex();
256
+
257
+ const isAsciiWordCharacter = (char: string): boolean => {
258
+ if (char === '_') {
259
+ return true;
260
+ }
261
+
262
+ const charCode = char.charCodeAt(0);
263
+ return (
264
+ (charCode >= 48 && charCode <= 57) ||
265
+ (charCode >= 65 && charCode <= 90) ||
266
+ (charCode >= 97 && charCode <= 122)
267
+ );
268
+ };
269
+
270
+ const isMidWordCharacter = (char: string): boolean => {
271
+ if (midWordCharacterRegex) {
272
+ return midWordCharacterRegex.test(char);
273
+ }
274
+
275
+ if (isAsciiWordCharacter(char)) {
276
+ return true;
277
+ }
278
+
279
+ return char.toLocaleLowerCase() !== char.toLocaleUpperCase();
280
+ };
281
+
282
+ const getLeadingTextCharacter = (text?: string): string | undefined => {
283
+ const firstCodePoint = text?.codePointAt(0);
284
+
285
+ if (firstCodePoint === undefined) {
286
+ return undefined;
287
+ }
288
+
289
+ return String.fromCodePoint(firstCodePoint);
290
+ };
291
+
246
292
  const getTypedLengthForPrediction = (textBefore: string): number => {
247
293
  if (isWordBoundary(textBefore)) {
248
294
  return 0;
@@ -514,6 +560,19 @@ export const createAutocompletePlugin = (
514
560
  return;
515
561
  }
516
562
 
563
+ // Suppress suggestions when the cursor is mid-word — only offer
564
+ // completions when the cursor is at the trailing edge of a token.
565
+ // nodeAfter correctly handles inline atoms (mentions, emojis) where
566
+ // parentOffset and textContent indices diverge.
567
+ const nodeAfter = selection.$from.nodeAfter;
568
+ const charAfterCursor = nodeAfter?.isText ? getLeadingTextCharacter(nodeAfter.text) : undefined;
569
+ // Only suppress for Unicode letters, digits, and underscore — hyphens,
570
+ // apostrophes, and similar punctuation are valid left-edge boundaries
571
+ // and should not block suggestions (e.g. cursor before '-' in compound-word).
572
+ if (charAfterCursor && isMidWordCharacter(charAfterCursor)) {
573
+ return;
574
+ }
575
+
517
576
  const textBefore = getTextBeforeCursor(state);
518
577
 
519
578
  // Suppress re-showing the same suggestion the user just dismissed.