@lvce-editor/editor-worker 15.1.0 → 16.0.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.
@@ -474,7 +474,7 @@ const getFirstEvent = (eventEmitter, eventMap) => {
474
474
  return promise;
475
475
  };
476
476
  const Message$1 = 3;
477
- const create$5$2 = async ({
477
+ const create$5$1 = async ({
478
478
  isMessagePortOpen,
479
479
  messagePort
480
480
  }) => {
@@ -525,61 +525,19 @@ const wrap$5 = messagePort => {
525
525
  };
526
526
  const IpcParentWithMessagePort$1 = {
527
527
  __proto__: null,
528
- create: create$5$2,
528
+ create: create$5$1,
529
529
  signal: signal$1,
530
530
  wrap: wrap$5
531
531
  };
532
532
 
533
- const Two = '2.0';
534
- const create$4$2 = (method, params) => {
535
- return {
536
- jsonrpc: Two,
537
- method,
538
- params
539
- };
540
- };
533
+ const Two$1 = '2.0';
541
534
  const callbacks = Object.create(null);
542
- const set$d = (id, fn) => {
543
- callbacks[id] = fn;
544
- };
545
535
  const get$8 = id => {
546
536
  return callbacks[id];
547
537
  };
548
538
  const remove$9 = id => {
549
539
  delete callbacks[id];
550
540
  };
551
- let id = 0;
552
- const create$3$2 = () => {
553
- return ++id;
554
- };
555
- const registerPromise = () => {
556
- const id = create$3$2();
557
- const {
558
- resolve,
559
- promise
560
- } = Promise.withResolvers();
561
- set$d(id, resolve);
562
- return {
563
- id,
564
- promise
565
- };
566
- };
567
- const create$2$1 = (method, params) => {
568
- const {
569
- id,
570
- promise
571
- } = registerPromise();
572
- const message = {
573
- jsonrpc: Two,
574
- method,
575
- params,
576
- id
577
- };
578
- return {
579
- message,
580
- promise
581
- };
582
- };
583
541
  class JsonRpcError extends Error {
584
542
  constructor(message) {
585
543
  super(message);
@@ -773,9 +731,9 @@ const getErrorProperty = (error, prettyError) => {
773
731
  }
774
732
  };
775
733
  };
776
- const create$1$1 = (id, error) => {
734
+ const create$1$2 = (id, error) => {
777
735
  return {
778
- jsonrpc: Two,
736
+ jsonrpc: Two$1,
779
737
  id,
780
738
  error
781
739
  };
@@ -784,22 +742,22 @@ const getErrorResponse = (id, error, preparePrettyError, logError) => {
784
742
  const prettyError = preparePrettyError(error);
785
743
  logError(error, prettyError);
786
744
  const errorProperty = getErrorProperty(error, prettyError);
787
- return create$1$1(id, errorProperty);
745
+ return create$1$2(id, errorProperty);
788
746
  };
789
- const create$b = (message, result) => {
747
+ const create$a = (message, result) => {
790
748
  return {
791
- jsonrpc: Two,
749
+ jsonrpc: Two$1,
792
750
  id: message.id,
793
751
  result: result ?? null
794
752
  };
795
753
  };
796
754
  const getSuccessResponse = (message, result) => {
797
755
  const resultProperty = result ?? null;
798
- return create$b(message, resultProperty);
756
+ return create$a(message, resultProperty);
799
757
  };
800
758
  const getErrorResponseSimple = (id, error) => {
801
759
  return {
802
- jsonrpc: Two,
760
+ jsonrpc: Two$1,
803
761
  id,
804
762
  error: {
805
763
  code: Custom,
@@ -886,29 +844,6 @@ const handleJsonRpcMessage = async (...args) => {
886
844
  }
887
845
  throw new JsonRpcError('unexpected message');
888
846
  };
889
- const invokeHelper = async (ipc, method, params, useSendAndTransfer) => {
890
- const {
891
- message,
892
- promise
893
- } = create$2$1(method, params);
894
- if (useSendAndTransfer && ipc.sendAndTransfer) {
895
- ipc.sendAndTransfer(message);
896
- } else {
897
- ipc.send(message);
898
- }
899
- const responseMessage = await promise;
900
- return unwrapJsonRpcResult(responseMessage);
901
- };
902
- const send$1 = (transport, method, ...params) => {
903
- const message = create$4$2(method, params);
904
- transport.send(message);
905
- };
906
- const invoke$e = (ipc, method, ...params) => {
907
- return invokeHelper(ipc, method, params, false);
908
- };
909
- const invokeAndTransfer$3 = (ipc, method, ...params) => {
910
- return invokeHelper(ipc, method, params, true);
911
- };
912
847
 
913
848
  class CommandNotFoundError extends Error {
914
849
  constructor(command) {
@@ -931,24 +866,87 @@ const execute$1 = (command, ...args) => {
931
866
  return fn(...args);
932
867
  };
933
868
 
869
+ const Two = '2.0';
870
+ const create$s = (method, params) => {
871
+ return {
872
+ jsonrpc: Two,
873
+ method,
874
+ params
875
+ };
876
+ };
877
+ const create$r = (id, method, params) => {
878
+ const message = {
879
+ id,
880
+ jsonrpc: Two,
881
+ method,
882
+ params
883
+ };
884
+ return message;
885
+ };
886
+ let id = 0;
887
+ const create$q = () => {
888
+ return ++id;
889
+ };
890
+
891
+ /* eslint-disable n/no-unsupported-features/es-syntax */
892
+
893
+ const registerPromise = map => {
894
+ const id = create$q();
895
+ const {
896
+ promise,
897
+ resolve
898
+ } = Promise.withResolvers();
899
+ map[id] = resolve;
900
+ return {
901
+ id,
902
+ promise
903
+ };
904
+ };
905
+
906
+ // @ts-ignore
907
+ const invokeHelper = async (callbacks, ipc, method, params, useSendAndTransfer) => {
908
+ const {
909
+ id,
910
+ promise
911
+ } = registerPromise(callbacks);
912
+ const message = create$r(id, method, params);
913
+ if (useSendAndTransfer && ipc.sendAndTransfer) {
914
+ ipc.sendAndTransfer(message);
915
+ } else {
916
+ ipc.send(message);
917
+ }
918
+ const responseMessage = await promise;
919
+ return unwrapJsonRpcResult(responseMessage);
920
+ };
934
921
  const createRpc = ipc => {
922
+ const callbacks = Object.create(null);
923
+ ipc._resolve = (id, response) => {
924
+ const fn = callbacks[id];
925
+ if (!fn) {
926
+ console.warn(`callback ${id} may already be disposed`);
927
+ return;
928
+ }
929
+ fn(response);
930
+ delete callbacks[id];
931
+ };
935
932
  const rpc = {
933
+ async dispose() {
934
+ await ipc?.dispose();
935
+ },
936
+ invoke(method, ...params) {
937
+ return invokeHelper(callbacks, ipc, method, params, false);
938
+ },
939
+ invokeAndTransfer(method, ...params) {
940
+ return invokeHelper(callbacks, ipc, method, params, true);
941
+ },
936
942
  // @ts-ignore
937
943
  ipc,
938
944
  /**
939
945
  * @deprecated
940
946
  */
941
947
  send(method, ...params) {
942
- send$1(ipc, method, ...params);
943
- },
944
- invoke(method, ...params) {
945
- return invoke$e(ipc, method, ...params);
946
- },
947
- invokeAndTransfer(method, ...params) {
948
- return invokeAndTransfer$3(ipc, method, ...params);
949
- },
950
- async dispose() {
951
- await ipc?.dispose();
948
+ const message = create$s(method, params);
949
+ ipc.send(message);
952
950
  }
953
951
  };
954
952
  return rpc;
@@ -965,7 +963,7 @@ const logError$1 = () => {
965
963
  const handleMessage = event => {
966
964
  const actualRequiresSocket = event?.target?.requiresSocket || requiresSocket;
967
965
  const actualExecute = event?.target?.execute || execute$1;
968
- return handleJsonRpcMessage(event.target, event.data, actualExecute, resolve, preparePrettyError, logError$1, actualRequiresSocket);
966
+ return handleJsonRpcMessage(event.target, event.data, actualExecute, event.target._resolve, preparePrettyError, logError$1, actualRequiresSocket);
969
967
  };
970
968
  const handleIpc = ipc => {
971
969
  if ('addEventListener' in ipc) {
@@ -983,35 +981,63 @@ const listen$1 = async (module, options) => {
983
981
  const ipc = module.wrap(rawIpc);
984
982
  return ipc;
985
983
  };
986
- const create$e = async ({
984
+
985
+ /* eslint-disable @typescript-eslint/no-misused-promises */
986
+
987
+ const createSharedLazyRpc = factory => {
988
+ let rpcPromise;
989
+ const getOrCreate = () => {
990
+ if (!rpcPromise) {
991
+ rpcPromise = factory();
992
+ }
993
+ return rpcPromise;
994
+ };
995
+ return {
996
+ async dispose() {
997
+ const rpc = await getOrCreate();
998
+ await rpc.dispose();
999
+ },
1000
+ async invoke(method, ...params) {
1001
+ const rpc = await getOrCreate();
1002
+ return rpc.invoke(method, ...params);
1003
+ },
1004
+ async invokeAndTransfer(method, ...params) {
1005
+ const rpc = await getOrCreate();
1006
+ return rpc.invokeAndTransfer(method, ...params);
1007
+ },
1008
+ async send(method, ...params) {
1009
+ const rpc = await getOrCreate();
1010
+ rpc.send(method, ...params);
1011
+ }
1012
+ };
1013
+ };
1014
+ const create$i = async ({
987
1015
  commandMap,
988
- messagePort,
989
- isMessagePortOpen
1016
+ isMessagePortOpen,
1017
+ send
990
1018
  }) => {
991
- // TODO create a commandMap per rpc instance
992
- register(commandMap);
993
- const rawIpc = await IpcParentWithMessagePort$1.create({
994
- messagePort,
995
- isMessagePortOpen
1019
+ return createSharedLazyRpc(() => {
1020
+ return create$2$1({
1021
+ commandMap,
1022
+ isMessagePortOpen,
1023
+ send
1024
+ });
996
1025
  });
997
- const ipc = IpcParentWithMessagePort$1.wrap(rawIpc);
998
- handleIpc(ipc);
999
- const rpc = createRpc(ipc);
1000
- return rpc;
1001
1026
  };
1002
- const MessagePortRpcParent = {
1027
+ const LazyTransferMessagePortRpcParent = {
1003
1028
  __proto__: null,
1004
- create: create$e
1029
+ create: create$i
1005
1030
  };
1006
- const create$5$1 = async ({
1031
+ const create$4$1 = async ({
1007
1032
  commandMap,
1033
+ isMessagePortOpen = true,
1008
1034
  messagePort
1009
1035
  }) => {
1010
1036
  // TODO create a commandMap per rpc instance
1011
1037
  register(commandMap);
1012
1038
  const rawIpc = await IpcParentWithMessagePort$1.create({
1013
- messagePort,
1014
- isMessagePortOpen: true
1039
+ isMessagePortOpen,
1040
+ messagePort
1015
1041
  });
1016
1042
  const ipc = IpcParentWithMessagePort$1.wrap(rawIpc);
1017
1043
  handleIpc(ipc);
@@ -1019,21 +1045,22 @@ const create$5$1 = async ({
1019
1045
  messagePort.start();
1020
1046
  return rpc;
1021
1047
  };
1022
- const create$4$1 = async ({
1048
+ const create$3$1 = async ({
1023
1049
  commandMap,
1024
1050
  messagePort
1025
1051
  }) => {
1026
- return create$5$1({
1052
+ return create$4$1({
1027
1053
  commandMap,
1028
1054
  messagePort
1029
1055
  });
1030
1056
  };
1031
1057
  const PlainMessagePortRpcParent = {
1032
1058
  __proto__: null,
1033
- create: create$4$1
1059
+ create: create$3$1
1034
1060
  };
1035
- const create$3$1 = async ({
1061
+ const create$2$1 = async ({
1036
1062
  commandMap,
1063
+ isMessagePortOpen,
1037
1064
  send
1038
1065
  }) => {
1039
1066
  const {
@@ -1041,16 +1068,17 @@ const create$3$1 = async ({
1041
1068
  port2
1042
1069
  } = new MessageChannel();
1043
1070
  await send(port1);
1044
- return create$5$1({
1071
+ return create$4$1({
1045
1072
  commandMap,
1073
+ isMessagePortOpen,
1046
1074
  messagePort: port2
1047
1075
  });
1048
1076
  };
1049
1077
  const TransferMessagePortRpcParent = {
1050
1078
  __proto__: null,
1051
- create: create$3$1
1079
+ create: create$2$1
1052
1080
  };
1053
- const create$a = async ({
1081
+ const create$1$1 = async ({
1054
1082
  commandMap
1055
1083
  }) => {
1056
1084
  // TODO create a commandMap per rpc instance
@@ -1062,7 +1090,7 @@ const create$a = async ({
1062
1090
  };
1063
1091
  const WebWorkerRpcClient = {
1064
1092
  __proto__: null,
1065
- create: create$a
1093
+ create: create$1$1
1066
1094
  };
1067
1095
  const createMockRpc = ({
1068
1096
  commandMap
@@ -1077,9 +1105,9 @@ const createMockRpc = ({
1077
1105
  return command(...params);
1078
1106
  };
1079
1107
  const mockRpc = {
1108
+ invocations,
1080
1109
  invoke,
1081
- invokeAndTransfer: invoke,
1082
- invocations
1110
+ invokeAndTransfer: invoke
1083
1111
  };
1084
1112
  return mockRpc;
1085
1113
  };
@@ -1141,14 +1169,15 @@ const DebugWorker = 55;
1141
1169
  const EditorWorker = 99;
1142
1170
  const ExtensionHostWorker = 44;
1143
1171
  const ExtensionManagementWorker = 9006;
1172
+ const IconThemeWorker = 7009;
1144
1173
  const MarkdownWorker = 300;
1145
- const RendererWorker = 1;
1174
+ const RendererWorker$1 = 1;
1146
1175
  const TextMeasurementWorker = 7011;
1147
1176
 
1148
1177
  const FocusEditorText$1 = 12;
1149
1178
 
1150
1179
  const rpcs = Object.create(null);
1151
- const set$c = (id, rpc) => {
1180
+ const set$d = (id, rpc) => {
1152
1181
  rpcs[id] = rpc;
1153
1182
  };
1154
1183
  const get$7 = id => {
@@ -1158,6 +1187,7 @@ const remove$8 = id => {
1158
1187
  delete rpcs[id];
1159
1188
  };
1160
1189
 
1190
+ /* eslint-disable @typescript-eslint/explicit-function-return-type */
1161
1191
  const create$9 = rpcId => {
1162
1192
  return {
1163
1193
  async dispose() {
@@ -1176,8 +1206,20 @@ const create$9 = rpcId => {
1176
1206
  // @ts-ignore
1177
1207
  return rpc.invokeAndTransfer(method, ...params);
1178
1208
  },
1209
+ registerMockRpc(commandMap) {
1210
+ const mockRpc = createMockRpc({
1211
+ commandMap
1212
+ });
1213
+ set$d(rpcId, mockRpc);
1214
+ // @ts-ignore
1215
+ mockRpc[Symbol.dispose] = () => {
1216
+ remove$8(rpcId);
1217
+ };
1218
+ // @ts-ignore
1219
+ return mockRpc;
1220
+ },
1179
1221
  set(rpc) {
1180
- set$c(rpcId, rpc);
1222
+ set$d(rpcId, rpc);
1181
1223
  }
1182
1224
  };
1183
1225
  };
@@ -1186,7 +1228,8 @@ const {
1186
1228
  dispose: dispose$2,
1187
1229
  invoke: invoke$d,
1188
1230
  invokeAndTransfer: invokeAndTransfer$2,
1189
- set: set$b
1231
+ registerMockRpc,
1232
+ set: set$c
1190
1233
  } = create$9(ExtensionHostWorker);
1191
1234
  const executeReferenceProvider = async (id, offset) => {
1192
1235
  // @ts-ignore
@@ -1200,12 +1243,9 @@ const getRuntimeStatus = async extensionId => {
1200
1243
  // @ts-ignore
1201
1244
  return invoke$d('ExtensionHost.getRuntimeStatus', extensionId);
1202
1245
  };
1203
- const registerMockRpc = commandMap => {
1204
- const mockRpc = createMockRpc({
1205
- commandMap
1206
- });
1207
- set$b(mockRpc);
1208
- return mockRpc;
1246
+ const getEnabledOutputProviderIds = async () => {
1247
+ const channels = await invoke$d('Output.getEnabledProviders');
1248
+ return channels;
1209
1249
  };
1210
1250
 
1211
1251
  const ExtensionHost = {
@@ -1213,23 +1253,24 @@ const ExtensionHost = {
1213
1253
  dispose: dispose$2,
1214
1254
  executeFileReferenceProvider,
1215
1255
  executeReferenceProvider,
1256
+ getEnabledOutputProviderIds,
1216
1257
  getRuntimeStatus,
1217
1258
  invoke: invoke$d,
1218
1259
  invokeAndTransfer: invokeAndTransfer$2,
1219
1260
  registerMockRpc,
1220
- set: set$b
1261
+ set: set$c
1221
1262
  };
1222
1263
 
1223
1264
  const {
1224
1265
  invoke: invoke$c,
1225
- set: set$a
1266
+ set: set$b
1226
1267
  } = create$9(ExtensionManagementWorker);
1227
1268
 
1228
1269
  const {
1229
1270
  invoke: invoke$b,
1230
1271
  invokeAndTransfer: invokeAndTransfer$1,
1231
- set: set$9
1232
- } = create$9(RendererWorker);
1272
+ set: set$a
1273
+ } = create$9(RendererWorker$1);
1233
1274
  const showContextMenu2 = async (uid, menuId, x, y, args) => {
1234
1275
  number(uid);
1235
1276
  number(menuId);
@@ -1248,6 +1289,10 @@ const sendMessagePortToExtensionManagementWorker = async (port, rpcId) => {
1248
1289
  await invokeAndTransfer$1('SendMessagePortToExtensionHostWorker.sendMessagePortToExtensionManagementWorker', port, command, rpcId);
1249
1290
  };
1250
1291
 
1292
+ const {
1293
+ set: set$9
1294
+ } = create$9(IconThemeWorker);
1295
+
1251
1296
  const {
1252
1297
  dispose: dispose$1,
1253
1298
  invoke: invoke$a,
@@ -1268,7 +1313,7 @@ const createLazyRpc = rpcId => {
1268
1313
  let factory;
1269
1314
  const createRpc = async () => {
1270
1315
  const rpc = await factory();
1271
- set$c(rpcId, rpc);
1316
+ set$d(rpcId, rpc);
1272
1317
  };
1273
1318
  const ensureRpc = async () => {
1274
1319
  if (!rpcPromise) {
@@ -1296,8 +1341,8 @@ const createLazyRpc = rpcId => {
1296
1341
  // TODO add tests for this
1297
1342
  const activateByEvent = async (event, assetDir, platform) => {
1298
1343
  string(event);
1299
- string(assetDir);
1300
- number(platform);
1344
+ // Assert.string(assetDir)
1345
+ // Assert.number(platform)
1301
1346
  await invoke$b('ExtensionHostManagement.activateByEvent', event, assetDir, platform);
1302
1347
  };
1303
1348
 
@@ -1306,39 +1351,22 @@ const codeGeneratorAccept = state => {
1306
1351
  return state;
1307
1352
  };
1308
1353
 
1309
- const getPortTuple = () => {
1310
- const {
1311
- port1,
1312
- port2
1313
- } = new MessageChannel();
1314
- return {
1315
- port1,
1316
- port2
1317
- };
1318
- };
1319
-
1320
1354
  const ModuleWorkerAndWorkaroundForChromeDevtoolsBug = 6;
1321
1355
 
1322
1356
  const launchWorker = async (name, url, intializeCommand) => {
1323
- // TODO use transferMessagePortRpc
1324
- const {
1325
- port1,
1326
- port2
1327
- } = getPortTuple();
1328
- // @ts-ignore
1329
- await invokeAndTransfer$1('IpcParent.create', {
1330
- method: ModuleWorkerAndWorkaroundForChromeDevtoolsBug,
1331
- name: name,
1332
- port: port1,
1333
- raw: true,
1334
- url
1335
- });
1336
- const rpc = await MessagePortRpcParent.create({
1357
+ const rpc = await TransferMessagePortRpcParent.create({
1337
1358
  commandMap: {},
1338
1359
  isMessagePortOpen: true,
1339
- messagePort: port2
1360
+ async send(port) {
1361
+ await invokeAndTransfer$1('IpcParent.create', {
1362
+ method: ModuleWorkerAndWorkaroundForChromeDevtoolsBug,
1363
+ name,
1364
+ port,
1365
+ raw: true,
1366
+ url
1367
+ });
1368
+ }
1340
1369
  });
1341
- port2.start();
1342
1370
  if (intializeCommand) {
1343
1371
  await rpc.invoke(intializeCommand);
1344
1372
  }
@@ -2142,8 +2170,29 @@ const getStartDefaults = (tokens, minOffset) => {
2142
2170
  startIndex
2143
2171
  };
2144
2172
  };
2145
- const getLineInfoEmbeddedFull = (embeddedResults, tokenResults, line, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset) => {
2173
+ const getLineInfoEmbeddedFull = (embeddedResults, tokenResults, line, decorations, lineOffset, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset) => {
2146
2174
  const lineInfo = [];
2175
+
2176
+ // Build decoration map for this line (position -> decoration class)
2177
+ const decorationMap = new Map();
2178
+ for (let j = 0; j < decorations.length; j += 4) {
2179
+ const decorationOffset = decorations[j];
2180
+ const decorationLength = decorations[j + 1];
2181
+ const decorationType = decorations[j + 2];
2182
+ const relativeStart = decorationOffset - lineOffset;
2183
+ const relativeEnd = relativeStart + decorationLength;
2184
+
2185
+ // Only include decorations that overlap with this line
2186
+ if (relativeStart < line.length && relativeEnd > 0) {
2187
+ const decorationClassName = getDecorationClassName(decorationType);
2188
+ if (decorationClassName) {
2189
+ decorationMap.set(Math.max(0, relativeStart), {
2190
+ className: decorationClassName,
2191
+ end: Math.min(line.length, relativeEnd)
2192
+ });
2193
+ }
2194
+ }
2195
+ }
2147
2196
  const embeddedResult = embeddedResults[tokenResults.embeddedResultIndex];
2148
2197
  const embeddedTokens = embeddedResult.result.tokens;
2149
2198
  const embeddedTokenMap = embeddedResult.TokenMap;
@@ -2157,12 +2206,66 @@ const getLineInfoEmbeddedFull = (embeddedResults, tokenResults, line, normalize,
2157
2206
  for (let i = startIndex; i < tokensLength; i += 2) {
2158
2207
  const tokenType = embeddedTokens[i];
2159
2208
  const tokenLength = embeddedTokens[i + 1];
2160
- end += tokenLength;
2161
- const className = `Token ${embeddedTokenMap[tokenType] || 'Unknown'}`;
2162
- const text = line.slice(start, end);
2163
- const normalizedText = normalizeText(text, normalize, tabSize);
2164
- lineInfo.push(normalizedText, className);
2165
- start = end;
2209
+ const tokenEnd = start + tokenLength;
2210
+
2211
+ // Check if any decorations overlap with this token
2212
+ let hasOverlap = false;
2213
+ for (const [decorationStart, {
2214
+ end: decorationEnd
2215
+ }] of decorationMap) {
2216
+ if (decorationStart < tokenEnd && decorationEnd > start) {
2217
+ hasOverlap = true;
2218
+ break;
2219
+ }
2220
+ }
2221
+ if (hasOverlap) {
2222
+ // Token has decoration overlap - split into parts
2223
+ let currentPos = start;
2224
+ while (currentPos < tokenEnd) {
2225
+ // Find if current position is inside a decoration
2226
+ let activeDecoration = null;
2227
+ for (const [decorationStart, decoration] of decorationMap) {
2228
+ if (decorationStart <= currentPos && decoration.end > currentPos) {
2229
+ activeDecoration = decoration;
2230
+ break;
2231
+ }
2232
+ }
2233
+ if (activeDecoration) {
2234
+ // Render decorated part
2235
+ const partEnd = Math.min(tokenEnd, activeDecoration.end);
2236
+ const text = line.slice(currentPos, partEnd);
2237
+ const baseTokenClass = embeddedTokenMap[tokenType] || 'Unknown';
2238
+ const className = `Token ${baseTokenClass} ${activeDecoration.className}`;
2239
+ const normalizedText = normalizeText(text, normalize, tabSize);
2240
+ lineInfo.push(normalizedText, className);
2241
+ currentPos = partEnd;
2242
+ } else {
2243
+ // Find next decoration start or token end
2244
+ let nextDecorationStart = tokenEnd;
2245
+ for (const [decorationStart] of decorationMap) {
2246
+ if (decorationStart > currentPos && decorationStart < tokenEnd) {
2247
+ nextDecorationStart = Math.min(nextDecorationStart, decorationStart);
2248
+ }
2249
+ }
2250
+
2251
+ // Render non-decorated part
2252
+ const partEnd = nextDecorationStart;
2253
+ const text = line.slice(currentPos, partEnd);
2254
+ const className = `Token ${embeddedTokenMap[tokenType] || 'Unknown'}`;
2255
+ const normalizedText = normalizeText(text, normalize, tabSize);
2256
+ lineInfo.push(normalizedText, className);
2257
+ currentPos = partEnd;
2258
+ }
2259
+ }
2260
+ } else {
2261
+ // No decoration overlap - render token normally
2262
+ const text = line.slice(start, tokenEnd);
2263
+ const className = `Token ${embeddedTokenMap[tokenType] || 'Unknown'}`;
2264
+ const normalizedText = normalizeText(text, normalize, tabSize);
2265
+ lineInfo.push(normalizedText, className);
2266
+ }
2267
+ start = tokenEnd;
2268
+ end = tokenEnd;
2166
2269
  if (end >= maxOffset) {
2167
2270
  break;
2168
2271
  }
@@ -2195,11 +2298,25 @@ const getDifference = (start, averageCharWidth, deltaX) => {
2195
2298
  };
2196
2299
  const getLineInfoDefault = (line, tokenResults, embeddedResults, decorations, TokenMap, lineOffset, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset) => {
2197
2300
  const lineInfo = [];
2198
- let decorationIndex = 0;
2199
- for (; decorationIndex < decorations.length; decorationIndex += 3) {
2200
- const decorationOffset = decorations[decorationIndex];
2201
- if (decorationOffset >= lineOffset) {
2202
- break;
2301
+
2302
+ // Build decoration map for this line (position -> decoration class)
2303
+ const decorationMap = new Map();
2304
+ for (let j = 0; j < decorations.length; j += 4) {
2305
+ const decorationOffset = decorations[j];
2306
+ const decorationLength = decorations[j + 1];
2307
+ const decorationType = decorations[j + 2];
2308
+ const relativeStart = decorationOffset - lineOffset;
2309
+ const relativeEnd = relativeStart + decorationLength;
2310
+
2311
+ // Only include decorations that overlap with this line
2312
+ if (relativeStart < line.length && relativeEnd > 0) {
2313
+ const decorationClassName = getDecorationClassName(decorationType);
2314
+ if (decorationClassName) {
2315
+ decorationMap.set(Math.max(0, relativeStart), {
2316
+ className: decorationClassName,
2317
+ end: Math.min(line.length, relativeEnd)
2318
+ });
2319
+ }
2203
2320
  }
2204
2321
  }
2205
2322
  const {
@@ -2215,27 +2332,66 @@ const getLineInfoDefault = (line, tokenResults, embeddedResults, decorations, To
2215
2332
  for (let i = startIndex; i < tokensLength; i += 2) {
2216
2333
  const tokenType = tokens[i];
2217
2334
  const tokenLength = tokens[i + 1];
2218
- const decorationOffset = decorations[decorationIndex];
2219
- let extraClassName = '';
2220
- if (decorationOffset !== undefined && decorationOffset - lineOffset === start) {
2221
- // @ts-ignore
2222
- decorations[++decorationIndex];
2223
- const decorationType = decorations[++decorationIndex];
2224
- // @ts-ignore
2225
- decorations[++decorationIndex];
2226
- // decorationIndex,
2227
- // decorationLength,
2228
- // decorationType,
2229
- // decorationModifiers,
2230
- // })
2231
- extraClassName = getDecorationClassName(decorationType);
2335
+ const tokenEnd = start + tokenLength;
2336
+
2337
+ // Check if any decorations overlap with this token
2338
+ let hasOverlap = false;
2339
+ for (const [decorationStart, {
2340
+ end: decorationEnd
2341
+ }] of decorationMap) {
2342
+ if (decorationStart < tokenEnd && decorationEnd > start) {
2343
+ hasOverlap = true;
2344
+ break;
2345
+ }
2232
2346
  }
2233
- end += tokenLength;
2234
- const text = line.slice(start, end);
2235
- const className = `Token ${extraClassName || TokenMap[tokenType] || 'Unknown'}`;
2236
- const normalizedText = normalizeText(text, normalize, tabSize);
2237
- lineInfo.push(normalizedText, className);
2238
- start = end;
2347
+ if (hasOverlap) {
2348
+ // Token has decoration overlap - split into parts
2349
+ let currentPos = start;
2350
+ while (currentPos < tokenEnd) {
2351
+ // Find if current position is inside a decoration
2352
+ let activeDecoration = null;
2353
+ for (const [decorationStart, decoration] of decorationMap) {
2354
+ if (decorationStart <= currentPos && decoration.end > currentPos) {
2355
+ activeDecoration = decoration;
2356
+ break;
2357
+ }
2358
+ }
2359
+ if (activeDecoration) {
2360
+ // Render decorated part
2361
+ const partEnd = Math.min(tokenEnd, activeDecoration.end);
2362
+ const text = line.slice(currentPos, partEnd);
2363
+ const baseTokenClass = TokenMap[tokenType] || 'Unknown';
2364
+ const className = `Token ${baseTokenClass} ${activeDecoration.className}`;
2365
+ const normalizedText = normalizeText(text, normalize, tabSize);
2366
+ lineInfo.push(normalizedText, className);
2367
+ currentPos = partEnd;
2368
+ } else {
2369
+ // Find next decoration start or token end
2370
+ let nextDecorationStart = tokenEnd;
2371
+ for (const [decorationStart] of decorationMap) {
2372
+ if (decorationStart > currentPos && decorationStart < tokenEnd) {
2373
+ nextDecorationStart = Math.min(nextDecorationStart, decorationStart);
2374
+ }
2375
+ }
2376
+
2377
+ // Render non-decorated part
2378
+ const partEnd = nextDecorationStart;
2379
+ const text = line.slice(currentPos, partEnd);
2380
+ const className = `Token ${TokenMap[tokenType] || 'Unknown'}`;
2381
+ const normalizedText = normalizeText(text, normalize, tabSize);
2382
+ lineInfo.push(normalizedText, className);
2383
+ currentPos = partEnd;
2384
+ }
2385
+ }
2386
+ } else {
2387
+ // No decoration overlap - render token normally
2388
+ const text = line.slice(start, tokenEnd);
2389
+ const className = `Token ${TokenMap[tokenType] || 'Unknown'}`;
2390
+ const normalizedText = normalizeText(text, normalize, tabSize);
2391
+ lineInfo.push(normalizedText, className);
2392
+ }
2393
+ start = tokenEnd;
2394
+ end = tokenEnd;
2239
2395
  if (end >= maxOffset) {
2240
2396
  break;
2241
2397
  }
@@ -2253,7 +2409,7 @@ const getLineInfo$1 = (line, tokenResults, embeddedResults, decorations, TokenMa
2253
2409
  if (embeddedResults.length > 0 && tokenResults.embeddedResultIndex !== undefined) {
2254
2410
  const embeddedResult = embeddedResults[tokenResults.embeddedResultIndex];
2255
2411
  if (embeddedResult?.isFull) {
2256
- return getLineInfoEmbeddedFull(embeddedResults, tokenResults, line, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset);
2412
+ return getLineInfoEmbeddedFull(embeddedResults, tokenResults, line, decorations, lineOffset, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset);
2257
2413
  }
2258
2414
  }
2259
2415
  return getLineInfoDefault(line, tokenResults, embeddedResults, decorations, TokenMap, lineOffset, normalize, tabSize, width, deltaX, averageCharWidth, minOffset, maxOffset);
@@ -2274,10 +2430,25 @@ const getLineInfosViewport = (editor, tokens, embeddedResults, minLineY, maxLine
2274
2430
  for (let i = minLineY; i < maxLineY; i++) {
2275
2431
  const line = lines[i];
2276
2432
  const normalize = shouldNormalizeText(line);
2433
+
2434
+ // Use decorations that were pre-computed (includes links and diagnostics)
2435
+ // Filter decorations to only include those for this line
2436
+ const lineDecorations = [];
2437
+ for (let j = 0; j < decorations.length; j += 4) {
2438
+ const decorationOffset = decorations[j];
2439
+ const decorationLength = decorations[j + 1];
2440
+ const decorationType = decorations[j + 2];
2441
+ const decorationModifiers = decorations[j + 3];
2442
+
2443
+ // Include decoration if it starts within this line
2444
+ if (decorationOffset >= offset && decorationOffset < offset + line.length) {
2445
+ lineDecorations.push(decorationOffset, decorationLength, decorationType, decorationModifiers);
2446
+ }
2447
+ }
2277
2448
  const {
2278
2449
  difference,
2279
2450
  lineInfo
2280
- } = getLineInfo$1(line, tokens[i - minLineY], embeddedResults, decorations, tokenMap, offset, normalize, tabSize, width, deltaX, averageCharWidth);
2451
+ } = getLineInfo$1(line, tokens[i - minLineY], embeddedResults, lineDecorations, tokenMap, offset, normalize, tabSize, width, deltaX, averageCharWidth);
2281
2452
  result.push(lineInfo);
2282
2453
  differences.push(difference);
2283
2454
  offset += line.length + 1;
@@ -2308,7 +2479,7 @@ const getVisible$1 = async (editor, syncIncremental) => {
2308
2479
  tokenizersToLoad,
2309
2480
  tokens
2310
2481
  } = await getTokensViewport2(editor, minLineY, maxLineY, syncIncremental);
2311
- const minLineOffset = offsetAtSync(editor, minLineY, 0);
2482
+ const minLineOffset = await offsetAtSync(editor, minLineY, 0);
2312
2483
  const averageCharWidth = charWidth;
2313
2484
  const {
2314
2485
  differences,
@@ -3137,6 +3308,71 @@ const getLanguages = async (platform, assetDir) => {
3137
3308
  return languages;
3138
3309
  };
3139
3310
 
3311
+ /**
3312
+ * Gets all regex matches for a given text and regex pattern
3313
+ * @param text The text to match against
3314
+ * @param regex The regex pattern to use (should have global flag)
3315
+ * @returns Array of regex matches
3316
+ */
3317
+ const getRegexMatches = (text, regex) => {
3318
+ return [...text.matchAll(regex)];
3319
+ };
3320
+
3321
+ // URL matching regex pattern - matches common URL schemes
3322
+ // Supports: http://, https://, ftp://, ftps://, file://
3323
+ // Also matches URLs without explicit scheme (www.example.com)
3324
+ const URL_PATTERN = /(?:(?:https?|ftp|ftps|file):\/\/)?(?:www\.)?(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?(?:\/[^\s]*)?/g;
3325
+
3326
+ // Regex to check if URL has a scheme (http://, https://, ftp://, etc.)
3327
+ const HAS_SCHEME_PATTERN = /^(?:https?|ftp|ftps|file):\/\//;
3328
+
3329
+ // Regex to check if URL starts with www.
3330
+ const HAS_WWW_PATTERN = /^www\./;
3331
+
3332
+ /**
3333
+ * Detects links in a given text and returns their positions
3334
+ * @param text The text to scan for links
3335
+ * @returns Array of links with their start position and length
3336
+ */
3337
+ const detectLinks = text => {
3338
+ const matches = getRegexMatches(text, URL_PATTERN);
3339
+ const links = [];
3340
+ for (const match of matches) {
3341
+ const url = match[0];
3342
+ // Only consider as link if it has a scheme or starts with www.
3343
+ if (HAS_SCHEME_PATTERN.test(url) || HAS_WWW_PATTERN.test(url)) {
3344
+ links.push({
3345
+ length: url.length,
3346
+ start: match.index ?? 0
3347
+ });
3348
+ }
3349
+ }
3350
+ return links;
3351
+ };
3352
+
3353
+ /**
3354
+ * Detects all links in an editor and returns them as decorations
3355
+ * @param editor The editor containing lines to scan
3356
+ * @returns Flat array of decorations in format [offset, length, type, modifiers, ...]
3357
+ */
3358
+ const detectAllLinksAsDecorations = editor => {
3359
+ const decorations = [];
3360
+ const {
3361
+ lines
3362
+ } = editor;
3363
+ let offset = 0;
3364
+ for (const line of lines) {
3365
+ const links = detectLinks(line);
3366
+ for (const link of links) {
3367
+ const linkOffset = offset + link.start;
3368
+ // Add link decoration: offset, length, type, modifiers
3369
+ decorations.push(linkOffset, link.length, Link, 0);
3370
+ }
3371
+ offset += line.length + 1; // +1 for newline
3372
+ }
3373
+ return decorations;
3374
+ };
3375
+
3140
3376
  const measureCharacterWidth = async (fontWeight, fontSize, fontFamily, letterSpacing) => {
3141
3377
  return await measureTextWidth('a', fontWeight, fontSize, fontFamily, letterSpacing, false, 0);
3142
3378
  };
@@ -3243,11 +3479,15 @@ const updateDiagnostics = async newState => {
3243
3479
  if (!latest) {
3244
3480
  return newState;
3245
3481
  }
3246
- const decorations = await getVisibleDiagnostics(latest.newState, diagnostics);
3482
+ const visualDecorations = await getVisibleDiagnostics(latest.newState, diagnostics);
3483
+ // Re-detect link decorations after text changes
3484
+ const linkDecorations = detectAllLinksAsDecorations(latest.newState);
3247
3485
  const newEditor = {
3248
3486
  ...latest.newState,
3249
- decorations,
3250
- diagnostics
3487
+ decorations: linkDecorations,
3488
+ // Text-level decorations (flat array) for CSS classes
3489
+ diagnostics,
3490
+ visualDecorations // Visual decorations (objects with x, y, width, height) for squiggly underlines
3251
3491
  };
3252
3492
  set$6(newState.id, latest.oldState, newEditor);
3253
3493
  // @ts-ignore
@@ -3376,18 +3616,28 @@ const createEditor = async ({
3376
3616
  } else {
3377
3617
  newEditor3 = await setDeltaY$2(newEditor2, 0);
3378
3618
  }
3619
+
3620
+ // Detect links and initialize decorations
3621
+ const linkDecorations = detectAllLinksAsDecorations(newEditor3);
3622
+ const newEditor3WithLinks = {
3623
+ ...newEditor3,
3624
+ decorations: linkDecorations
3625
+ };
3379
3626
  const syncIncremental = getEnabled();
3380
3627
  const {
3381
3628
  differences,
3382
3629
  textInfos
3383
- } = await getVisible$1(newEditor3, syncIncremental);
3630
+ } = await getVisible$1(newEditor3WithLinks, syncIncremental);
3384
3631
  const newEditor4 = {
3385
- ...newEditor3,
3632
+ ...newEditor3WithLinks,
3386
3633
  differences,
3387
3634
  focus: FocusEditorText$1,
3388
3635
  focused: true,
3389
3636
  textInfos
3390
3637
  };
3638
+ console.error({
3639
+ newEditor4
3640
+ });
3391
3641
  set$6(id, emptyEditor, newEditor4);
3392
3642
 
3393
3643
  // TODO only sync when needed
@@ -5280,7 +5530,7 @@ const handleContextMenu = async (editor, button, x, y) => {
5280
5530
  const {
5281
5531
  uid
5282
5532
  } = editor;
5283
- await showContextMenu2(uid, Editor, /* x */x, /* y */y, {
5533
+ await showContextMenu2(uid, Editor, x, y, {
5284
5534
  menuId: Editor
5285
5535
  });
5286
5536
  return editor;
@@ -6301,7 +6551,7 @@ const launch = async () => {
6301
6551
  return;
6302
6552
  }
6303
6553
  const rpc = await launchFindWidgetWorker();
6304
- set$c(rpcId, rpc);
6554
+ set$d(rpcId, rpc);
6305
6555
  };
6306
6556
  const invoke$3 = async (method, ...params) => {
6307
6557
  const rpc = get$7(rpcId);
@@ -8591,6 +8841,9 @@ const getLineInfos = (lines, tokenizer, languageId) => {
8591
8841
  lineInfos.push(lineInfo);
8592
8842
  currentLineState = result;
8593
8843
  }
8844
+ console.error({
8845
+ lineInfos
8846
+ });
8594
8847
  return lineInfos;
8595
8848
  };
8596
8849
 
@@ -9796,7 +10049,7 @@ const handleMessagePort = async (port, rpcId) => {
9796
10049
  messagePort: port
9797
10050
  });
9798
10051
  if (rpcId) {
9799
- set$c(rpcId, rpc);
10052
+ set$d(rpcId, rpc);
9800
10053
  }
9801
10054
  };
9802
10055
 
@@ -9945,7 +10198,7 @@ const createExtensionManagementWorkerRpc = async () => {
9945
10198
  const initializeExtensionManagementWorker = async () => {
9946
10199
  try {
9947
10200
  const rpc = await createExtensionManagementWorkerRpc();
9948
- set$a(rpc);
10201
+ set$b(rpc);
9949
10202
  } catch {
9950
10203
  // ignore
9951
10204
  }
@@ -9986,6 +10239,22 @@ const initializeSyntaxHighlighting = async (syntaxHighlightingEnabled, syncIncre
9986
10239
  }
9987
10240
  };
9988
10241
 
10242
+ const send$1 = port => {
10243
+ // @ts-ignore
10244
+ return RendererWorker.sendMessagePortToTextMeasurementWorker(port);
10245
+ };
10246
+ const initializeTextMeasurementWorker = async () => {
10247
+ try {
10248
+ const rpc = await LazyTransferMessagePortRpcParent.create({
10249
+ commandMap: {},
10250
+ send: send$1
10251
+ });
10252
+ set$9(rpc);
10253
+ } catch {
10254
+ // ignore
10255
+ }
10256
+ };
10257
+
9989
10258
  const launchCompletionWorker = async () => {
9990
10259
  const name = 'Completion Worker';
9991
10260
  const url = 'completionWorkerMain.js';
@@ -9996,7 +10265,7 @@ const launchCompletionWorker = async () => {
9996
10265
 
9997
10266
  const intialize = async (syntaxHighlightingEnabled, syncIncremental) => {
9998
10267
  setFactory(launchCompletionWorker);
9999
- await Promise.all([initializeSyntaxHighlighting(syntaxHighlightingEnabled, syncIncremental), initializeExtensionHost(), initializeExtensionManagementWorker()]);
10268
+ await Promise.all([initializeSyntaxHighlighting(syntaxHighlightingEnabled, syncIncremental), initializeExtensionHost(), initializeExtensionManagementWorker(), initializeTextMeasurementWorker()]);
10000
10269
  };
10001
10270
 
10002
10271
  // TODO move cursor
@@ -10210,6 +10479,9 @@ const renderLines = {
10210
10479
  } = newState;
10211
10480
  const relativeLine = highlightedLine - minLineY;
10212
10481
  const dom = getEditorRowsVirtualDom(textInfos, differences, true, relativeLine);
10482
+ console.error({
10483
+ dom
10484
+ });
10213
10485
  return [/* method */'setText', dom];
10214
10486
  },
10215
10487
  isEqual(oldState, newState) {
@@ -10280,11 +10552,11 @@ const renderAdditionalFocusContext = {
10280
10552
  };
10281
10553
  const renderDecorations = {
10282
10554
  apply(oldState, newState) {
10283
- const dom = getDiagnosticsVirtualDom(newState.decorations);
10555
+ const dom = getDiagnosticsVirtualDom(newState.visualDecorations || []);
10284
10556
  return ['setDecorationsDom', dom];
10285
10557
  },
10286
10558
  isEqual(oldState, newState) {
10287
- return oldState.decorations === newState.decorations;
10559
+ return oldState.visualDecorations === newState.visualDecorations;
10288
10560
  }
10289
10561
  };
10290
10562
  const renderGutterInfo = {
@@ -10465,7 +10737,7 @@ const updateDebugInfo = async debugId => {
10465
10737
  await invoke$b('Editor.rerender', key);
10466
10738
  };
10467
10739
 
10468
- const keep = ['ActivateByEvent.activateByEvent', 'ExtensionHostManagement.activateByEvent', 'Editor.applyEdit2', 'Editor.applyEdits2', 'Editor.closeFind2', 'Editor.closeWidget2', 'Editor.create', 'Editor.getKeyBindings', 'Editor.getSourceActions', 'Editor.getLines2', 'Editor.getPositionAtCursor', 'Editor.getOffsetAtCursor', 'Editor.getQuickPickMenuEntries', 'Editor.getSelections', 'Editor.hotReload', 'Editor.getSelections2', 'Editor.getDiagnostics', 'Editor.getText', 'Editor.getWordAt', 'Editor.getWordAt2', 'Editor.getWordAtOffset2', 'Editor.getUri', 'Editor.getWordBefore', 'Editor.getWordBefore2', 'Editor.offsetAt', 'Editor.render', 'Editor.setSelections2', 'Editor.updateDebugInfo', 'Editor.getLanguageId', 'Editor.getProblems', 'Editor.getKeys', 'Editor.getMenuIds', 'Font.ensure', 'HandleMessagePort.handleMessagePort', 'Hover.getHoverInfo', 'Hover.handleSashPointerDown', 'Hover.handleSashPointerMove', 'Hover.handleSashPointerUp', 'Hover.loadContent', 'Hover.render', 'Initialize.initialize', 'SendMessagePortToExtensionHostWorker.sendMessagePortToExtensionHostWorker'
10740
+ const keep = ['ActivateByEvent.activateByEvent', 'ExtensionHostManagement.activateByEvent', 'Editor.applyEdit2', 'Editor.applyEdits2', 'Editor.closeFind2', 'Editor.closeWidget2', 'Editor.create', 'Editor.getKeyBindings', 'Editor.getSourceActions', 'Editor.getLines2', 'Editor.getPositionAtCursor', 'Editor.getOffsetAtCursor', 'Editor.getQuickPickMenuEntries', 'Editor.getSelections', 'Editor.hotReload', 'Editor.getSelections2', 'Editor.getDiagnostics', 'Editor.getText', 'Editor.getWordAt', 'Editor.getWordAt2', 'Editor.getWordAtOffset2', 'Editor.getUri', 'Editor.getWordBefore', 'Editor.getWordBefore2', 'Editor.offsetAt', 'Editor.render', 'Editor.setSelections2', 'Editor.updateDebugInfo', 'Editor.getLanguageId', 'Editor.getProblems', 'Editor.getKeys', 'Editor.getMenuIds', 'Editor.getMenuEntries', 'Editor.getMenuEntries2', 'Font.ensure', 'HandleMessagePort.handleMessagePort', 'Hover.getHoverInfo', 'Hover.handleSashPointerDown', 'Hover.handleSashPointerMove', 'Hover.handleSashPointerUp', 'Hover.loadContent', 'Hover.render', 'Initialize.initialize', 'SendMessagePortToExtensionHostWorker.sendMessagePortToExtensionHostWorker'
10469
10741
  // 'ColorPicker.handleSliderPointerDown',
10470
10742
  // 'ColorPicker.handleSliderPointerMove',
10471
10743
  // 'ColorPicker.loadContent',
@@ -10787,7 +11059,7 @@ const listen = async () => {
10787
11059
  const rpc = await WebWorkerRpcClient.create({
10788
11060
  commandMap: commandMap
10789
11061
  });
10790
- set$9(rpc);
11062
+ set$a(rpc);
10791
11063
  };
10792
11064
 
10793
11065
  const CodeGeneratorInput = 'CodeGeneratorInput';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/editor-worker",
3
- "version": "15.1.0",
3
+ "version": "16.0.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git@github.com:lvce-editor/editor-worker.git"