@pierre/diffs 1.3.0-beta.8 → 1.3.0-rc.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.
Files changed (229) hide show
  1. package/dist/components/CodeView.d.ts +12 -12
  2. package/dist/components/CodeView.d.ts.map +1 -1
  3. package/dist/components/CodeView.js +51 -43
  4. package/dist/components/CodeView.js.map +1 -1
  5. package/dist/components/File.d.ts +7 -2
  6. package/dist/components/File.d.ts.map +1 -1
  7. package/dist/components/File.js +36 -8
  8. package/dist/components/File.js.map +1 -1
  9. package/dist/components/FileDiff.d.ts +34 -6
  10. package/dist/components/FileDiff.d.ts.map +1 -1
  11. package/dist/components/FileDiff.js +198 -17
  12. package/dist/components/FileDiff.js.map +1 -1
  13. package/dist/components/FileStream.d.ts.map +1 -1
  14. package/dist/components/FileStream.js +2 -1
  15. package/dist/components/FileStream.js.map +1 -1
  16. package/dist/components/VirtualizedFile.d.ts +2 -1
  17. package/dist/components/VirtualizedFile.d.ts.map +1 -1
  18. package/dist/components/VirtualizedFile.js +9 -5
  19. package/dist/components/VirtualizedFile.js.map +1 -1
  20. package/dist/components/VirtualizedFileDiff.d.ts +14 -0
  21. package/dist/components/VirtualizedFileDiff.d.ts.map +1 -1
  22. package/dist/components/VirtualizedFileDiff.js +61 -3
  23. package/dist/components/VirtualizedFileDiff.js.map +1 -1
  24. package/dist/components/Virtualizer.d.ts +14 -2
  25. package/dist/components/Virtualizer.d.ts.map +1 -1
  26. package/dist/components/Virtualizer.js +29 -3
  27. package/dist/components/Virtualizer.js.map +1 -1
  28. package/dist/edit/index.d.ts +5 -0
  29. package/dist/edit/index.js +3 -0
  30. package/dist/editor/command.d.ts +1 -1
  31. package/dist/editor/command.d.ts.map +1 -1
  32. package/dist/editor/command.js +11 -0
  33. package/dist/editor/command.js.map +1 -1
  34. package/dist/editor/editStack.d.ts +6 -2
  35. package/dist/editor/editStack.d.ts.map +1 -1
  36. package/dist/editor/editStack.js +54 -13
  37. package/dist/editor/editStack.js.map +1 -1
  38. package/dist/editor/editor.d.ts +51 -23
  39. package/dist/editor/editor.d.ts.map +1 -1
  40. package/dist/editor/editor.js +995 -242
  41. package/dist/editor/editor.js.map +1 -1
  42. package/dist/editor/editor2.js +1 -1
  43. package/dist/editor/languages.d.ts +26 -0
  44. package/dist/editor/languages.d.ts.map +1 -0
  45. package/dist/editor/languages.js +363 -0
  46. package/dist/editor/languages.js.map +1 -0
  47. package/dist/editor/lineAnnotations.d.ts.map +1 -1
  48. package/dist/editor/lineAnnotations.js +25 -26
  49. package/dist/editor/lineAnnotations.js.map +1 -1
  50. package/dist/editor/marker.d.ts +5 -3
  51. package/dist/editor/marker.d.ts.map +1 -1
  52. package/dist/editor/marker.js +111 -91
  53. package/dist/editor/marker.js.map +1 -1
  54. package/dist/editor/matchBrackets.d.ts +3 -2
  55. package/dist/editor/matchBrackets.d.ts.map +1 -1
  56. package/dist/editor/matchBrackets.js +2 -10
  57. package/dist/editor/matchBrackets.js.map +1 -1
  58. package/dist/editor/pieceTable.d.ts +2 -1
  59. package/dist/editor/pieceTable.d.ts.map +1 -1
  60. package/dist/editor/pieceTable.js +103 -39
  61. package/dist/editor/pieceTable.js.map +1 -1
  62. package/dist/editor/platform.js +2 -0
  63. package/dist/editor/platform.js.map +1 -1
  64. package/dist/editor/popover.d.ts +25 -5
  65. package/dist/editor/popover.d.ts.map +1 -1
  66. package/dist/editor/popover.js +60 -14
  67. package/dist/editor/popover.js.map +1 -1
  68. package/dist/editor/searchPanel.d.ts +1 -0
  69. package/dist/editor/searchPanel.d.ts.map +1 -1
  70. package/dist/editor/searchPanel.js +15 -8
  71. package/dist/editor/searchPanel.js.map +1 -1
  72. package/dist/editor/selection.d.ts +31 -11
  73. package/dist/editor/selection.d.ts.map +1 -1
  74. package/dist/editor/selection.js +198 -70
  75. package/dist/editor/selection.js.map +1 -1
  76. package/dist/editor/selectionAction.d.ts +4 -3
  77. package/dist/editor/selectionAction.d.ts.map +1 -1
  78. package/dist/editor/selectionAction.js +9 -5
  79. package/dist/editor/selectionAction.js.map +1 -1
  80. package/dist/editor/stateStorage.d.ts +15 -0
  81. package/dist/editor/stateStorage.d.ts.map +1 -0
  82. package/dist/editor/stateStorage.js +65 -0
  83. package/dist/editor/stateStorage.js.map +1 -0
  84. package/dist/editor/textDocument.d.ts +12 -77
  85. package/dist/editor/textDocument.d.ts.map +1 -1
  86. package/dist/editor/textDocument.js +76 -34
  87. package/dist/editor/textDocument.js.map +1 -1
  88. package/dist/editor/textMeasure.d.ts +7 -12
  89. package/dist/editor/textMeasure.d.ts.map +1 -1
  90. package/dist/editor/textMeasure.js +40 -32
  91. package/dist/editor/textMeasure.js.map +1 -1
  92. package/dist/editor/{tokenzier.d.ts → tokenizer.d.ts} +3 -3
  93. package/dist/editor/tokenizer.d.ts.map +1 -0
  94. package/dist/editor/{tokenzier.js → tokenizer.js} +91 -26
  95. package/dist/editor/tokenizer.js.map +1 -0
  96. package/dist/editor/utils.js +1 -1
  97. package/dist/editor/utils.js.map +1 -1
  98. package/dist/index.d.ts +5 -4
  99. package/dist/index.js +7 -6
  100. package/dist/managers/InteractionManager.d.ts +18 -7
  101. package/dist/managers/InteractionManager.d.ts.map +1 -1
  102. package/dist/managers/InteractionManager.js +154 -99
  103. package/dist/managers/InteractionManager.js.map +1 -1
  104. package/dist/managers/UniversalRenderingManager.d.ts +2 -1
  105. package/dist/managers/UniversalRenderingManager.d.ts.map +1 -1
  106. package/dist/managers/UniversalRenderingManager.js +7 -3
  107. package/dist/managers/UniversalRenderingManager.js.map +1 -1
  108. package/dist/react/CodeView.d.ts +13 -13
  109. package/dist/react/CodeView.d.ts.map +1 -1
  110. package/dist/react/CodeView.js +38 -29
  111. package/dist/react/CodeView.js.map +1 -1
  112. package/dist/react/EditContext.d.ts +20 -0
  113. package/dist/react/EditContext.d.ts.map +1 -0
  114. package/dist/react/EditContext.js +20 -0
  115. package/dist/react/EditContext.js.map +1 -0
  116. package/dist/react/File.d.ts +2 -1
  117. package/dist/react/File.d.ts.map +1 -1
  118. package/dist/react/File.js +3 -2
  119. package/dist/react/File.js.map +1 -1
  120. package/dist/react/FileDiff.d.ts +2 -2
  121. package/dist/react/FileDiff.d.ts.map +1 -1
  122. package/dist/react/FileDiff.js +3 -2
  123. package/dist/react/FileDiff.js.map +1 -1
  124. package/dist/react/MultiFileDiff.d.ts +2 -2
  125. package/dist/react/MultiFileDiff.d.ts.map +1 -1
  126. package/dist/react/MultiFileDiff.js +3 -2
  127. package/dist/react/MultiFileDiff.js.map +1 -1
  128. package/dist/react/PatchDiff.d.ts +2 -2
  129. package/dist/react/PatchDiff.d.ts.map +1 -1
  130. package/dist/react/PatchDiff.js +3 -2
  131. package/dist/react/PatchDiff.js.map +1 -1
  132. package/dist/react/UnresolvedFile.d.ts +1 -1
  133. package/dist/react/UnresolvedFile.js.map +1 -1
  134. package/dist/react/index.d.ts +4 -4
  135. package/dist/react/index.js +3 -3
  136. package/dist/react/types.d.ts +9 -1
  137. package/dist/react/types.d.ts.map +1 -1
  138. package/dist/react/utils/useFileDiffInstance.d.ts +5 -3
  139. package/dist/react/utils/useFileDiffInstance.d.ts.map +1 -1
  140. package/dist/react/utils/useFileDiffInstance.js +28 -27
  141. package/dist/react/utils/useFileDiffInstance.js.map +1 -1
  142. package/dist/react/utils/useFileInstance.d.ts +5 -3
  143. package/dist/react/utils/useFileInstance.d.ts.map +1 -1
  144. package/dist/react/utils/useFileInstance.js +29 -25
  145. package/dist/react/utils/useFileInstance.js.map +1 -1
  146. package/dist/react/utils/useUnresolvedFileInstance.js +3 -3
  147. package/dist/react/utils/useUnresolvedFileInstance.js.map +1 -1
  148. package/dist/renderers/DiffHunksRenderer.d.ts +33 -1
  149. package/dist/renderers/DiffHunksRenderer.d.ts.map +1 -1
  150. package/dist/renderers/DiffHunksRenderer.js +134 -21
  151. package/dist/renderers/DiffHunksRenderer.js.map +1 -1
  152. package/dist/renderers/FileRenderer.d.ts +2 -0
  153. package/dist/renderers/FileRenderer.d.ts.map +1 -1
  154. package/dist/renderers/FileRenderer.js +33 -11
  155. package/dist/renderers/FileRenderer.js.map +1 -1
  156. package/dist/ssr/FileDiffReact.js +1 -1
  157. package/dist/ssr/index.d.ts +2 -2
  158. package/dist/ssr/preloadDiffs.js +1 -1
  159. package/dist/style.js +1 -1
  160. package/dist/types.d.ts +185 -42
  161. package/dist/types.d.ts.map +1 -1
  162. package/dist/utils/annotationHelpers.d.ts +20 -0
  163. package/dist/utils/annotationHelpers.d.ts.map +1 -0
  164. package/dist/utils/annotationHelpers.js +29 -0
  165. package/dist/utils/annotationHelpers.js.map +1 -0
  166. package/dist/utils/computeFileOffsets.d.ts.map +1 -1
  167. package/dist/utils/computeFileOffsets.js +34 -5
  168. package/dist/utils/computeFileOffsets.js.map +1 -1
  169. package/dist/utils/editSessionHunks.d.ts +66 -0
  170. package/dist/utils/editSessionHunks.d.ts.map +1 -0
  171. package/dist/utils/editSessionHunks.js +437 -0
  172. package/dist/utils/editSessionHunks.js.map +1 -0
  173. package/dist/utils/getHunkSideBoundaries.d.ts +7 -0
  174. package/dist/utils/getHunkSideBoundaries.d.ts.map +1 -0
  175. package/dist/utils/getHunkSideBoundaries.js +12 -0
  176. package/dist/utils/getHunkSideBoundaries.js.map +1 -0
  177. package/dist/utils/getTotalLineCountFromHunks.d.ts.map +1 -1
  178. package/dist/utils/getTotalLineCountFromHunks.js +2 -1
  179. package/dist/utils/getTotalLineCountFromHunks.js.map +1 -1
  180. package/dist/utils/hydratePartialDiff.d.ts.map +1 -1
  181. package/dist/utils/hydratePartialDiff.js +4 -3
  182. package/dist/utils/hydratePartialDiff.js.map +1 -1
  183. package/dist/utils/isGutterUtilityPath.d.ts +5 -0
  184. package/dist/utils/isGutterUtilityPath.d.ts.map +1 -0
  185. package/dist/utils/isGutterUtilityPath.js +12 -0
  186. package/dist/utils/isGutterUtilityPath.js.map +1 -0
  187. package/dist/utils/iterateOverDiff.d.ts.map +1 -1
  188. package/dist/utils/iterateOverDiff.js +17 -12
  189. package/dist/utils/iterateOverDiff.js.map +1 -1
  190. package/dist/utils/parseMergeConflictDiffFromFile.d.ts.map +1 -1
  191. package/dist/utils/parseMergeConflictDiffFromFile.js +9 -6
  192. package/dist/utils/parseMergeConflictDiffFromFile.js.map +1 -1
  193. package/dist/utils/parsePatchFiles.d.ts.map +1 -1
  194. package/dist/utils/parsePatchFiles.js +6 -3
  195. package/dist/utils/parsePatchFiles.js.map +1 -1
  196. package/dist/utils/realignChangeContent.d.ts +33 -0
  197. package/dist/utils/realignChangeContent.d.ts.map +1 -0
  198. package/dist/utils/realignChangeContent.js +148 -0
  199. package/dist/utils/realignChangeContent.js.map +1 -0
  200. package/dist/utils/renderDiffWithHighlighter.js +1 -1
  201. package/dist/utils/resolveRegion.d.ts.map +1 -1
  202. package/dist/utils/resolveRegion.js +18 -2
  203. package/dist/utils/resolveRegion.js.map +1 -1
  204. package/dist/utils/updateDiffHunks.d.ts +8 -2
  205. package/dist/utils/updateDiffHunks.d.ts.map +1 -1
  206. package/dist/utils/updateDiffHunks.js +9 -7
  207. package/dist/utils/updateDiffHunks.js.map +1 -1
  208. package/dist/utils/virtualDiffLayout.d.ts +42 -2
  209. package/dist/utils/virtualDiffLayout.d.ts.map +1 -1
  210. package/dist/utils/virtualDiffLayout.js +101 -7
  211. package/dist/utils/virtualDiffLayout.js.map +1 -1
  212. package/dist/worker/WorkerPoolManager.d.ts.map +1 -1
  213. package/dist/worker/WorkerPoolManager.js +4 -2
  214. package/dist/worker/WorkerPoolManager.js.map +1 -1
  215. package/dist/worker/types.d.ts +4 -0
  216. package/dist/worker/types.d.ts.map +1 -1
  217. package/dist/worker/worker-portable.js +60 -20
  218. package/dist/worker/worker-portable.js.map +1 -1
  219. package/dist/worker/worker.js +61 -19
  220. package/dist/worker/worker.js.map +1 -1
  221. package/package.json +13 -8
  222. package/dist/editor/index.d.ts +0 -3
  223. package/dist/editor/index.js +0 -3
  224. package/dist/editor/tokenzier.d.ts.map +0 -1
  225. package/dist/editor/tokenzier.js.map +0 -1
  226. package/dist/react/EditorContext.d.ts +0 -15
  227. package/dist/react/EditorContext.d.ts.map +0 -1
  228. package/dist/react/EditorContext.js +0 -23
  229. package/dist/react/EditorContext.js.map +0 -1
@@ -0,0 +1,15 @@
1
+ import { EditorState } from "../types.js";
2
+
3
+ //#region src/editor/stateStorage.d.ts
4
+ interface IStateStorage {
5
+ /** Read the editor state stored for a file cache key. */
6
+ get(cacheKey: string): EditorState | undefined | Promise<EditorState | undefined>;
7
+ /** Store the editor state for a file cache key. */
8
+ set(cacheKey: string, state: EditorState): void | Promise<void>;
9
+ }
10
+ type PersistStateStorage = 'inMemory' | 'indexedDB' | IStateStorage;
11
+ declare function cloneEditorState(state: EditorState): EditorState;
12
+ declare function createStateStorage(storage: PersistStateStorage): IStateStorage;
13
+ //#endregion
14
+ export { IStateStorage, PersistStateStorage, cloneEditorState, createStateStorage };
15
+ //# sourceMappingURL=stateStorage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stateStorage.d.ts","names":[],"sources":["../../src/editor/stateStorage.ts"],"mappings":";;;UAEiB,aAAA;;EAEf,GAAA,CACE,QAAA,WACC,WAAA,eAA0B,OAAA,CAAQ,WAAA;EAJT;EAM5B,GAAA,CAAI,QAAA,UAAkB,KAAA,EAAO,WAAA,UAAqB,OAAA;AAAA;AAAA,KAGxC,mBAAA,8BAAiD,aAAa;AAAA,iBAE1D,gBAAA,CAAiB,KAAA,EAAO,WAAA,GAAc,WAAW;AAAA,iBAWjD,kBAAA,CACd,OAAA,EAAS,mBAAA,GACR,aAAa"}
@@ -0,0 +1,65 @@
1
+ //#region src/editor/stateStorage.ts
2
+ function cloneEditorState(state) {
3
+ return {
4
+ selections: state.selections?.map((selection) => ({
5
+ start: { ...selection.start },
6
+ end: { ...selection.end },
7
+ direction: selection.direction
8
+ })),
9
+ view: state.view === void 0 ? void 0 : { ...state.view }
10
+ };
11
+ }
12
+ function createStateStorage(storage) {
13
+ return typeof storage === "object" ? storage : storage === "indexedDB" ? new IndexedDBStateStorage() : new InMemoryStateStorage();
14
+ }
15
+ var InMemoryStateStorage = class {
16
+ #states = /* @__PURE__ */ new Map();
17
+ get(cacheKey) {
18
+ const state = this.#states.get(cacheKey);
19
+ return state === void 0 ? void 0 : cloneEditorState(state);
20
+ }
21
+ set(cacheKey, state) {
22
+ this.#states.set(cacheKey, cloneEditorState(state));
23
+ }
24
+ };
25
+ const DATABASE_NAME = "pierre-diffs-editor-state";
26
+ const DATABASE_VERSION = 1;
27
+ const STORE_NAME = "states";
28
+ var IndexedDBStateStorage = class {
29
+ #database = openDatabase();
30
+ async get(cacheKey) {
31
+ const database = await this.#database;
32
+ if (database === void 0) return;
33
+ const state = await requestToPromise(database.transaction(STORE_NAME, "readonly").objectStore(STORE_NAME).get(cacheKey));
34
+ return state === void 0 ? void 0 : cloneEditorState(state);
35
+ }
36
+ async set(cacheKey, state) {
37
+ const database = await this.#database;
38
+ if (database === void 0) return;
39
+ await requestToPromise(database.transaction(STORE_NAME, "readwrite").objectStore(STORE_NAME).put(cloneEditorState(state), cacheKey));
40
+ }
41
+ };
42
+ function openDatabase() {
43
+ try {
44
+ const factory = globalThis.indexedDB;
45
+ if (factory === void 0) return Promise.resolve(void 0);
46
+ const request = factory.open(DATABASE_NAME, DATABASE_VERSION);
47
+ request.onupgradeneeded = () => {
48
+ const database = request.result;
49
+ if (!database.objectStoreNames.contains(STORE_NAME)) database.createObjectStore(STORE_NAME);
50
+ };
51
+ return requestToPromise(request);
52
+ } catch {
53
+ return Promise.resolve(void 0);
54
+ }
55
+ }
56
+ function requestToPromise(request) {
57
+ return new Promise((resolve, reject) => {
58
+ request.onsuccess = () => resolve(request.result);
59
+ request.onerror = () => reject(request.error);
60
+ });
61
+ }
62
+ //#endregion
63
+ export { cloneEditorState, createStateStorage };
64
+
65
+ //# sourceMappingURL=stateStorage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stateStorage.js","names":["#states","#database"],"sources":["../../src/editor/stateStorage.ts"],"sourcesContent":["import type { EditorState } from '../types';\n\nexport interface IStateStorage {\n /** Read the editor state stored for a file cache key. */\n get(\n cacheKey: string\n ): EditorState | undefined | Promise<EditorState | undefined>;\n /** Store the editor state for a file cache key. */\n set(cacheKey: string, state: EditorState): void | Promise<void>;\n}\n\nexport type PersistStateStorage = 'inMemory' | 'indexedDB' | IStateStorage;\n\nexport function cloneEditorState(state: EditorState): EditorState {\n return {\n selections: state.selections?.map((selection) => ({\n start: { ...selection.start },\n end: { ...selection.end },\n direction: selection.direction,\n })),\n view: state.view === undefined ? undefined : { ...state.view },\n };\n}\n\nexport function createStateStorage(\n storage: PersistStateStorage\n): IStateStorage {\n return typeof storage === 'object'\n ? storage\n : storage === 'indexedDB'\n ? new IndexedDBStateStorage()\n : new InMemoryStateStorage();\n}\n\nclass InMemoryStateStorage implements IStateStorage {\n #states = new Map<string, EditorState>();\n\n get(cacheKey: string): EditorState | undefined {\n const state = this.#states.get(cacheKey);\n return state === undefined ? undefined : cloneEditorState(state);\n }\n\n set(cacheKey: string, state: EditorState): void {\n this.#states.set(cacheKey, cloneEditorState(state));\n }\n}\n\nconst DATABASE_NAME = 'pierre-diffs-editor-state';\nconst DATABASE_VERSION = 1;\nconst STORE_NAME = 'states';\n\nclass IndexedDBStateStorage implements IStateStorage {\n #database = openDatabase();\n\n async get(cacheKey: string): Promise<EditorState | undefined> {\n const database = await this.#database;\n if (database === undefined) {\n return undefined;\n }\n const state = await requestToPromise<EditorState | undefined>(\n database\n .transaction(STORE_NAME, 'readonly')\n .objectStore(STORE_NAME)\n .get(cacheKey)\n );\n return state === undefined ? undefined : cloneEditorState(state);\n }\n\n async set(cacheKey: string, state: EditorState): Promise<void> {\n const database = await this.#database;\n if (database === undefined) {\n return;\n }\n await requestToPromise(\n database\n .transaction(STORE_NAME, 'readwrite')\n .objectStore(STORE_NAME)\n .put(cloneEditorState(state), cacheKey)\n );\n }\n}\n\nfunction openDatabase(): Promise<IDBDatabase | undefined> {\n try {\n const factory = globalThis.indexedDB;\n if (factory === undefined) {\n return Promise.resolve(undefined);\n }\n\n const request = factory.open(DATABASE_NAME, DATABASE_VERSION);\n request.onupgradeneeded = () => {\n const database = request.result;\n if (!database.objectStoreNames.contains(STORE_NAME)) {\n database.createObjectStore(STORE_NAME);\n }\n };\n return requestToPromise(request);\n } catch {\n return Promise.resolve(undefined);\n }\n}\n\nfunction requestToPromise<T>(request: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n}\n"],"mappings":";AAaA,SAAgB,iBAAiB,OAAiC;CAChE,OAAO;EACL,YAAY,MAAM,YAAY,KAAK,eAAe;GAChD,OAAO,EAAE,GAAG,UAAU,MAAM;GAC5B,KAAK,EAAE,GAAG,UAAU,IAAI;GACxB,WAAW,UAAU;EACvB,EAAE;EACF,MAAM,MAAM,SAAS,KAAA,IAAY,KAAA,IAAY,EAAE,GAAG,MAAM,KAAK;CAC/D;AACF;AAEA,SAAgB,mBACd,SACe;CACf,OAAO,OAAO,YAAY,WACtB,UACA,YAAY,cACV,IAAI,sBAAsB,IAC1B,IAAI,qBAAqB;AACjC;AAEA,IAAM,uBAAN,MAAoD;CAClD,0BAAU,IAAI,IAAyB;CAEvC,IAAI,UAA2C;EAC7C,MAAM,QAAQ,KAAKA,QAAQ,IAAI,QAAQ;EACvC,OAAO,UAAU,KAAA,IAAY,KAAA,IAAY,iBAAiB,KAAK;CACjE;CAEA,IAAI,UAAkB,OAA0B;EAC9C,KAAKA,QAAQ,IAAI,UAAU,iBAAiB,KAAK,CAAC;CACpD;AACF;AAEA,MAAM,gBAAgB;AACtB,MAAM,mBAAmB;AACzB,MAAM,aAAa;AAEnB,IAAM,wBAAN,MAAqD;CACnD,YAAY,aAAa;CAEzB,MAAM,IAAI,UAAoD;EAC5D,MAAM,WAAW,MAAM,KAAKC;EAC5B,IAAI,aAAa,KAAA,GACf;EAEF,MAAM,QAAQ,MAAM,iBAClB,SACG,YAAY,YAAY,UAAU,CAAC,CACnC,YAAY,UAAU,CAAC,CACvB,IAAI,QAAQ,CACjB;EACA,OAAO,UAAU,KAAA,IAAY,KAAA,IAAY,iBAAiB,KAAK;CACjE;CAEA,MAAM,IAAI,UAAkB,OAAmC;EAC7D,MAAM,WAAW,MAAM,KAAKA;EAC5B,IAAI,aAAa,KAAA,GACf;EAEF,MAAM,iBACJ,SACG,YAAY,YAAY,WAAW,CAAC,CACpC,YAAY,UAAU,CAAC,CACvB,IAAI,iBAAiB,KAAK,GAAG,QAAQ,CAC1C;CACF;AACF;AAEA,SAAS,eAAiD;CACxD,IAAI;EACF,MAAM,UAAU,WAAW;EAC3B,IAAI,YAAY,KAAA,GACd,OAAO,QAAQ,QAAQ,KAAA,CAAS;EAGlC,MAAM,UAAU,QAAQ,KAAK,eAAe,gBAAgB;EAC5D,QAAQ,wBAAwB;GAC9B,MAAM,WAAW,QAAQ;GACzB,IAAI,CAAC,SAAS,iBAAiB,SAAS,UAAU,GAChD,SAAS,kBAAkB,UAAU;EAEzC;EACA,OAAO,iBAAiB,OAAO;CACjC,QAAQ;EACN,OAAO,QAAQ,QAAQ,KAAA,CAAS;CAClC;AACF;AAEA,SAAS,iBAAoB,SAAoC;CAC/D,OAAO,IAAI,SAAS,SAAS,WAAW;EACtC,QAAQ,kBAAkB,QAAQ,QAAQ,MAAM;EAChD,QAAQ,gBAAgB,OAAO,QAAQ,KAAK;CAC9C,CAAC;AACH"}
@@ -1,79 +1,8 @@
1
- import { DiffLineAnnotation } from "../types.js";
2
- import { SearchParams } from "./searchPanel.js";
3
- import { EditorSelection } from "./selection.js";
1
+ import { DiffLineAnnotation, EditorSelection, Position, Range, TextEdit } from "../types.js";
4
2
  import { EditStack } from "./editStack.js";
3
+ import { SearchParams } from "./searchPanel.js";
5
4
 
6
5
  //#region src/editor/textDocument.d.ts
7
- /**
8
- * Position in a text document expressed as zero-based line and character offset.
9
- * The offsets are based on a UTF-16 string representation. So a string of the form
10
- * `a𐐀b` the character offset of the character `a` is 0, the character offset of `𐐀`
11
- * is 1 and the character offset of b is 3 since `𐐀` is represented using two code
12
- * units in UTF-16.
13
- *
14
- * Positions are line end character agnostic. So you can not specify a position that
15
- * denotes `\r|\n` or `\n|` where `|` represents the character offset.
16
- */
17
- interface Position {
18
- /**
19
- * Line position in a document (zero-based).
20
- *
21
- * If a line number is greater than the number of lines in a document, it
22
- * defaults back to the number of lines in the document.
23
- * If a line number is negative, it defaults to 0.
24
- *
25
- * The above two properties are implementation specific.
26
- */
27
- readonly line: number;
28
- /**
29
- * Character offset on a line in a document (zero-based).
30
- *
31
- * The meaning of this offset is determined by the negotiated
32
- * `PositionEncodingKind`.
33
- *
34
- * If the character value is greater than the line length it defaults back
35
- * to the line length. This property is implementation specific.
36
- */
37
- readonly character: number;
38
- }
39
- /**
40
- * A range in a text document expressed as (zero-based) start and end positions.
41
- *
42
- * If you want to specify a range that contains a line including the line ending
43
- * character(s) then use an end position denoting the start of the next line.
44
- * For example:
45
- * ```ts
46
- * {
47
- * start: { line: 5, character: 23 }
48
- * end : { line 6, character : 0 }
49
- * }
50
- * ```
51
- */
52
- interface Range {
53
- /**
54
- * The range's start position.
55
- */
56
- readonly start: Position;
57
- /**
58
- * The range's end position.
59
- */
60
- readonly end: Position;
61
- }
62
- /**
63
- * A text edit applicable to a text document.
64
- */
65
- interface TextEdit {
66
- /**
67
- * The range of the text document to be manipulated. To insert
68
- * text into a document create a range where start === end.
69
- */
70
- readonly range: Range;
71
- /**
72
- * The string to be inserted. For delete operations use an
73
- * empty string.
74
- */
75
- readonly newText: string;
76
- }
77
6
  /** Different with `TextEdit`, the range has been resolved to offsets. */
78
7
  interface ResolvedTextEdit {
79
8
  /** The start offset of the text change. */
@@ -91,8 +20,12 @@ interface TextDocumentChange {
91
20
  readonly startLine: number;
92
21
  /** Character on the first changed line where the edit began. */
93
22
  readonly startCharacter: number;
23
+ /** Character on the original last changed line where the edit ended. */
24
+ readonly endCharacter: number;
94
25
  /** Last line whose rendered content may have changed after the edit. */
95
26
  readonly endLine: number;
27
+ /** Whether the original edit range ended at the previous document EOF. */
28
+ readonly endedAtDocumentEnd: boolean;
96
29
  /** Line count before the edit was applied. */
97
30
  readonly previousLineCount: number;
98
31
  /** Line count after the edit was applied. */
@@ -102,8 +35,9 @@ interface TextDocumentChange {
102
35
  /** Exact rendered line ranges touched by each edit after the edit was applied. */
103
36
  readonly changedLineRanges: readonly [startLine: number, endLine: number][];
104
37
  /** Per-edit rendered line ranges before adjacent ranges are coalesced. */
105
- readonly changedLineChanges?: readonly [startLine: number, endLine: number, lineDelta: number][];
38
+ readonly changedLineChanges?: readonly [startLine: number, endLine: number, lineDelta: number, startCharacter?: number, endCharacter?: number, endedAtDocumentEnd?: boolean][];
106
39
  }
40
+ type TextDocumentHistoryResult<LAnnotation> = [change: TextDocumentChange, selections?: EditorSelection[], lineAnnotations?: DiffLineAnnotation<LAnnotation>[], selectionEdits?: ResolvedTextEdit[]];
107
41
  /**
108
42
  * A vscode-languageserver-textdocument compatible text document.
109
43
  */
@@ -130,13 +64,14 @@ declare class TextDocument<LAnnotation> {
130
64
  findNextNonOverlappingSubstring(needle: string, occupied: readonly [start: number, end: number][]): number | undefined;
131
65
  search(searchParams: SearchParams): [start: number, end: number][];
132
66
  applyEdits(edits: TextEdit[], updateHistory?: boolean, selectionsBefore?: EditorSelection[], selectionsAfter?: EditorSelection[], undoBoundary?: boolean): TextDocumentChange | undefined;
67
+ resolveEdits(edits: readonly TextEdit[]): ResolvedTextEdit[];
133
68
  applyResolvedEdits(edits: ResolvedTextEdit[], updateHistory?: boolean, selectionsBefore?: EditorSelection[], selectionsAfter?: EditorSelection[], undoBoundary?: boolean): TextDocumentChange | undefined;
134
69
  setLastUndoSelectionsAfter(selections: EditorSelection[]): void;
135
70
  setLastUndoLineAnnotations(lineAnnotationsBefore: DiffLineAnnotation<LAnnotation>[], lineAnnotationsAfter: DiffLineAnnotation<LAnnotation>[]): void;
136
- undo(): [change: TextDocumentChange, selections?: EditorSelection[], lineAnnotations?: DiffLineAnnotation<LAnnotation>[]] | undefined;
137
- redo(): [change: TextDocumentChange, selections?: EditorSelection[], lineAnnotations?: DiffLineAnnotation<LAnnotation>[]] | undefined;
71
+ undo(): TextDocumentHistoryResult<LAnnotation> | undefined;
72
+ redo(): TextDocumentHistoryResult<LAnnotation> | undefined;
138
73
  normalizePosition(position: Position): Position;
139
74
  }
140
75
  //#endregion
141
- export { Position, Range, ResolvedTextEdit, TextDocument, TextDocumentChange, TextEdit };
76
+ export { type Position, type Range, ResolvedTextEdit, TextDocument, TextDocumentChange, type TextEdit };
142
77
  //# sourceMappingURL=textDocument.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"textDocument.d.ts","names":[],"sources":["../../src/editor/textDocument.ts"],"mappings":";;;;;;;;AAsBA;;;;AAoBoB;AAgBpB;;;UApCiB,QAAA;EAwCN;;;;;AAIa;AAMxB;;;EAVW,SA9BA,IAAA;EA6CA;;;;AAKO;AAIlB;;;;EATW,SAnCA,SAAS;AAAA;;;AAqDL;AAGf;;;;;;;;;;UAxCiB,KAAA;EAsDuB;;;EAAA,SAlD7B,KAAA,EAAO,QAAA;EAsDd;;;EAAA,SAlDO,GAAA,EAAK,QAAQ;AAAA;;;;UAMP,QAAA;EAmHa;;;;EAAA,SA9GnB,KAAA,EAAO,KAAK;EA6KA;;;;EAAA,SAxKZ,OAAA;AAAA;;UAIM,gBAAA;EAiMZ;EAAA,SA/LM,KAAA;EAyOmC;EAAA,SAvOnC,GAAA;EAwOkC;;;;EAAA,SAnOlC,IAAA;AAAA;AAAA,UAGM,kBAAA;EAkQI;EAAA,SAhQV,SAAA;EAiQe;EAAA,SA/Pf,cAAA;EAkR8B;EAAA,SAhR9B,OAAA;EAgRsC;EAAA,SA9QtC,iBAAA;;WAEA,SAAA;EAwBP;EAAA,SAtBO,SAAA;EAwBP;EAAA,SAtBO,iBAAA,YAA6B,SAAA,UAAmB,OAAA;EAwB5C;EAAA,SAtBJ,kBAAA,aACP,SAAA,UACA,OAAA,UACA,SAAA;AAAA;;;;cAOS,YAAA;EAAA;EAOX,WAAA,CACE,GAAA,UACA,IAAA,UACA,UAAA,WACA,OAAA,WACA,SAAA,GAAW,SAAA,CAAU,WAAA;EAAA,IASnB,GAAA;EAAA,IAIA,UAAA;EAAA,IAIA,OAAA;EAAA,IAIA,SAAA;EAAA,IASA,GAAA;EAAA,IAaA,OAAA;EAAA,IAIA,OAAA;EAIJ,UAAA,CAAW,MAAA,WAAiB,QAAA;EAI5B,WAAA,CAAY,OAAA,sBAA6B,QAAA;EAIzC,QAAA,CAAS,QAAA,EAAU,QAAA;EAInB,OAAA,CAAQ,KAAA,GAAQ,KAAA;EAgBhB,WAAA,CAAY,IAAA,UAAc,gBAAA;EAO1B,YAAA,CAAa,IAAA;EAIb,aAAA,CAAc,IAAA,UAAc,gBAAA;EAI5B,MAAA,CAAO,MAAA;EACP,MAAA,CAAO,QAAA,EAAU,QAAA;EAQjB,YAAA,CAAa,KAAA,UAAe,GAAA;EAI5B,+BAAA,CACE,MAAA,UACA,QAAA,YAAoB,KAAA,UAAe,GAAA;EAKrC,MAAA,CAAO,YAAA,EAAc,YAAA,IAAgB,KAAA,UAAe,GAAA;EAIpD,UAAA,CACE,KAAA,EAAO,QAAA,IACP,aAAA,YACA,gBAAA,GAAmB,eAAA,IACnB,eAAA,GAAkB,eAAA,IAClB,YAAA,aACC,kBAAA;EAaH,kBAAA,CACE,KAAA,EAAO,gBAAA,IACP,aAAA,YACA,gBAAA,GAAmB,eAAA,IACnB,eAAA,GAAkB,eAAA,IAClB,YAAA,aACC,kBAAA;EAqCH,0BAAA,CAA2B,UAAA,EAAY,eAAA;EAIvC,0BAAA,CACE,qBAAA,EAAuB,kBAAA,CAAmB,WAAA,KAC1C,oBAAA,EAAsB,kBAAA,CAAmB,WAAA;EAQ3C,IAAA,KAEM,MAAA,EAAQ,kBAAA,EACR,UAAA,GAAa,eAAA,IACb,eAAA,GAAkB,kBAAA,CAAmB,WAAA;EAmB3C,IAAA,KAEM,MAAA,EAAQ,kBAAA,EACR,UAAA,GAAa,eAAA,IACb,eAAA,GAAkB,kBAAA,CAAmB,WAAA;EAmB3C,iBAAA,CAAkB,QAAA,EAAU,QAAA,GAAW,QAAA;AAAA"}
1
+ {"version":3,"file":"textDocument.d.ts","names":[],"sources":["../../src/editor/textDocument.ts"],"mappings":";;;;;;UAoBiB,gBAAA;EAAgB;EAAA,SAEtB,KAAA;EAFsB;EAAA,SAItB,GAAA;EAAA;;;AAKI;EALJ,SAKA,IAAA;AAAA;AAAA,UAGM,kBAAA;EAAkB;EAAA,SAExB,SAAA;EAEA;EAAA,SAAA,cAAA;EAIA;EAAA,SAFA,YAAA;EAMA;EAAA,SAJA,OAAA;EAQA;EAAA,SANA,kBAAA;EAQ6B;EAAA,SAN7B,iBAAA;EAQA;EAAA,SANA,SAAA;EAQP;EAAA,SANO,SAAA;EAQP;EAAA,SANO,iBAAA,YAA6B,SAAA,UAAmB,OAAA;EAQvD;EAAA,SANO,kBAAA,aACP,SAAA,UACA,OAAA,UACA,SAAA,UACA,cAAA,WACA,YAAA,WACA,kBAAA;AAAA;AAAA,KAMC,yBAAA,iBACH,MAAA,EAAQ,kBAAA,EACR,UAAA,GAAa,eAAA,IACb,eAAA,GAAkB,kBAAA,CAAmB,WAAA,KACrC,cAAA,GAAiB,gBAAA;;;;cAMN,YAAA;EAAA;EAQX,WAAA,CACE,GAAA,UACA,IAAA,UACA,UAAA,WACA,OAAA,WACA,SAAA,GAAW,SAAA,CAAU,WAAA;EAAA,IAsBnB,GAAA;EAAA,IAIA,UAAA;EAAA,IAIA,OAAA;EAAA,IAIA,SAAA;EAAA,IAIA,GAAA;EAAA,IAIA,OAAA;EAAA,IAIA,OAAA;EAIJ,UAAA,CAAW,MAAA,WAAiB,QAAA;EAI5B,WAAA,CAAY,OAAA,sBAA6B,QAAA;EAQzC,QAAA,CAAS,QAAA,EAAU,QAAA;EAInB,OAAA,CAAQ,KAAA,GAAQ,KAAA;EAgBhB,WAAA,CAAY,IAAA,UAAc,gBAAA;EAO1B,YAAA,CAAa,IAAA;EAIb,aAAA,CAAc,IAAA,UAAc,gBAAA;EAI5B,MAAA,CAAO,MAAA;EACP,MAAA,CAAO,QAAA,EAAU,QAAA;EAQjB,YAAA,CAAa,KAAA,UAAe,GAAA;EAI5B,+BAAA,CACE,MAAA,UACA,QAAA,YAAoB,KAAA,UAAe,GAAA;EAKrC,MAAA,CAAO,YAAA,EAAc,YAAA,IAAgB,KAAA,UAAe,GAAA;EAIpD,UAAA,CACE,KAAA,EAAO,QAAA,IACP,aAAA,YACA,gBAAA,GAAmB,eAAA,IACnB,eAAA,GAAkB,eAAA,IAClB,YAAA,aACC,kBAAA;EAgBH,YAAA,CAAa,KAAA,WAAgB,QAAA,KAAa,gBAAA;EAI1C,kBAAA,CACE,KAAA,EAAO,gBAAA,IACP,aAAA,YACA,gBAAA,GAAmB,eAAA,IACnB,eAAA,GAAkB,eAAA,IAClB,YAAA,aACC,kBAAA;EA2DH,0BAAA,CAA2B,UAAA,EAAY,eAAA;EAIvC,0BAAA,CACE,qBAAA,EAAuB,kBAAA,CAAmB,WAAA,KAC1C,oBAAA,EAAsB,kBAAA,CAAmB,WAAA;EAQ3C,IAAA,IAAQ,yBAAA,CAA0B,WAAA;EAqBlC,IAAA,IAAQ,yBAAA,CAA0B,WAAA;EAqBlC,iBAAA,CAAkB,QAAA,EAAU,QAAA,GAAW,QAAA;AAAA"}
@@ -5,18 +5,23 @@ import { PieceTable } from "./pieceTable.js";
5
5
  /**
6
6
  * A vscode-languageserver-textdocument compatible text document.
7
7
  */
8
- var TextDocument = class {
8
+ var TextDocument = class TextDocument {
9
9
  #uri;
10
10
  #languageId;
11
11
  #version;
12
12
  #pieceTable;
13
13
  #editStack;
14
- constructor(uri, text, languageId = "plaintext", version = 0, editStack = new EditStack()) {
14
+ #eol;
15
+ constructor(uri, text, languageId = "text", version = 0, editStack = new EditStack()) {
15
16
  this.#uri = new URL(uri, "file://").toString();
16
17
  this.#languageId = languageId;
17
18
  this.#version = version;
18
19
  this.#pieceTable = new PieceTable(text);
19
20
  this.#editStack = editStack;
21
+ const firstLineBreak = this.#pieceTable.getLineText(0, true);
22
+ if (firstLineBreak.endsWith("\r\n")) this.#eol = "\r\n";
23
+ else if (firstLineBreak.endsWith("\r")) this.#eol = "\r";
24
+ else this.#eol = "\n";
20
25
  }
21
26
  get uri() {
22
27
  return this.#uri;
@@ -31,12 +36,7 @@ var TextDocument = class {
31
36
  return this.#pieceTable.lineCount;
32
37
  }
33
38
  get eol() {
34
- if (this.lineCount > 1) {
35
- const firstLineBreak = this.getLineText(0, true);
36
- if (firstLineBreak.endsWith("\r\n")) return "\r\n";
37
- if (firstLineBreak.endsWith("\r")) return "\r";
38
- }
39
- return "\n";
39
+ return this.#eol;
40
40
  }
41
41
  get canUndo() {
42
42
  return this.#editStack.canUndo;
@@ -45,10 +45,12 @@ var TextDocument = class {
45
45
  return this.#editStack.canRedo;
46
46
  }
47
47
  positionAt(offset) {
48
- return this.#pieceTable.positionAt(offset);
48
+ return this.normalizePosition(this.#pieceTable.positionAt(offset));
49
49
  }
50
50
  positionsAt(offsets) {
51
- return this.#pieceTable.positionsAt(offsets);
51
+ const positions = this.#pieceTable.positionsAt(offsets);
52
+ for (let i = 0; i < positions.length; i++) positions[i] = this.normalizePosition(positions[i]);
53
+ return positions;
52
54
  }
53
55
  offsetAt(position) {
54
56
  return this.#pieceTable.offsetAt(this.normalizePosition(position));
@@ -82,25 +84,25 @@ var TextDocument = class {
82
84
  search(searchParams) {
83
85
  return this.#pieceTable.search(searchParams);
84
86
  }
85
- applyEdits(edits, updateHistory = false, selectionsBefore, selectionsAfter, undoBoundary = false) {
87
+ applyEdits(edits, updateHistory = true, selectionsBefore, selectionsAfter, undoBoundary = false) {
86
88
  if (edits.length === 0) return;
87
- return this.applyResolvedEdits(edits.map((edit) => this.#resolveEdit(edit)), updateHistory, selectionsBefore, selectionsAfter, undoBoundary);
89
+ return this.#applyResolvedEdits(this.#sortAndValidateResolvedEdits(this.resolveEdits(edits)), updateHistory, selectionsBefore, selectionsAfter, undoBoundary);
90
+ }
91
+ resolveEdits(edits) {
92
+ return edits.map((edit) => this.#resolveEdit(edit));
88
93
  }
89
- applyResolvedEdits(edits, updateHistory = false, selectionsBefore, selectionsAfter, undoBoundary = false) {
94
+ applyResolvedEdits(edits, updateHistory = true, selectionsBefore, selectionsAfter, undoBoundary = false) {
90
95
  if (edits.length === 0) return;
91
- const resolvedEdits = this.#sortAndValidateResolvedEdits(edits);
92
- if (updateHistory) {
93
- const entry = createEditStackEntry(this, resolvedEdits, this.#version, this.#version + 1, selectionsBefore, selectionsAfter);
94
- if (undoBoundary) entry.undoBoundary = true;
95
- const previousEntry = this.#editStack.peekUndo();
96
- const change = this.#applyResolvedEditsToBuffer(resolvedEdits);
97
- this.#version++;
98
- if (change.lineDelta === 0 && shouldCoalesceEditStackEntry(previousEntry, entry)) this.#editStack.replaceLastUndo(coalesceEditStackEntries(previousEntry, entry));
99
- else this.#editStack.push(entry);
100
- return change;
101
- }
96
+ return this.#applyResolvedEdits(this.#sortAndValidateResolvedEdits(edits.map((edit) => this.#normalizeResolvedEdit(edit))), updateHistory, selectionsBefore, selectionsAfter, undoBoundary);
97
+ }
98
+ #applyResolvedEdits(resolvedEdits, updateHistory, selectionsBefore, selectionsAfter, undoBoundary) {
99
+ const entry = createEditStackEntry(this, resolvedEdits, this.#version, this.#version + 1, updateHistory ? selectionsBefore : void 0, updateHistory ? selectionsAfter : void 0);
100
+ if (updateHistory && undoBoundary) entry.undoBoundary = true;
101
+ const previousEntry = this.#editStack.peekUndoForCoalescing();
102
102
  const change = this.#applyResolvedEditsToBuffer(resolvedEdits);
103
103
  this.#version++;
104
+ if (change.lineDelta === 0 && shouldCoalesceEditStackEntry(previousEntry, entry)) this.#editStack.replaceLastUndo(coalesceEditStackEntries(previousEntry, entry));
105
+ else this.#editStack.push(entry);
104
106
  return change;
105
107
  }
106
108
  setLastUndoSelectionsAfter(selections) {
@@ -115,10 +117,12 @@ var TextDocument = class {
115
117
  const change = this.#applyResolvedEditsToBuffer(entry.inverseEdits);
116
118
  if (change === void 0) return;
117
119
  this.#version = entry.versionBefore;
120
+ const selections = entry.selectionsBefore?.slice();
118
121
  return [
119
122
  change,
120
- entry.selectionsBefore?.slice(),
121
- entry.lineAnnotationsBefore?.slice()
123
+ selections,
124
+ entry.lineAnnotationsBefore?.slice(),
125
+ selections === void 0 ? entry.inverseEdits.map((edit) => ({ ...edit })) : void 0
122
126
  ];
123
127
  }
124
128
  redo() {
@@ -127,19 +131,25 @@ var TextDocument = class {
127
131
  const change = this.#applyResolvedEditsToBuffer(entry.forwardEdits);
128
132
  if (change === void 0) return;
129
133
  this.#version = entry.versionAfter;
134
+ const selections = entry.selectionsAfter?.slice();
130
135
  return [
131
136
  change,
132
- entry.selectionsAfter?.slice(),
133
- entry.lineAnnotationsAfter?.slice()
137
+ selections,
138
+ entry.lineAnnotationsAfter?.slice(),
139
+ selections === void 0 ? entry.forwardEdits.map((edit) => ({ ...edit })) : void 0
134
140
  ];
135
141
  }
136
142
  normalizePosition(position) {
137
- const line = Math.max(0, Math.min(position.line, this.lineCount - 1));
143
+ const line = TextDocument.#clampIndex(position.line, this.lineCount - 1);
138
144
  return {
139
145
  line,
140
- character: Math.max(0, Math.min(position.character, this.getLineLength(line)))
146
+ character: TextDocument.#clampIndex(position.character, this.getLineLength(line))
141
147
  };
142
148
  }
149
+ static #clampIndex(value, max) {
150
+ if (Number.isNaN(value)) return 0;
151
+ return Math.max(0, Math.min(Math.floor(value), max));
152
+ }
143
153
  #resolveEdit(edit) {
144
154
  let start = this.offsetAt(edit.range.start);
145
155
  let end = this.offsetAt(edit.range.end);
@@ -148,14 +158,34 @@ var TextDocument = class {
148
158
  start = end;
149
159
  end = t;
150
160
  }
151
- return {
161
+ return this.#normalizeResolvedEdit({
152
162
  start,
153
163
  end,
154
164
  text: edit.newText
165
+ });
166
+ }
167
+ #normalizeResolvedEdit(edit) {
168
+ let { start, end } = edit;
169
+ const isInsertion = start === end;
170
+ if (this.#isInsideSurrogatePair(start)) start--;
171
+ if (isInsertion) end = start;
172
+ else if (this.#isInsideSurrogatePair(end)) end++;
173
+ return {
174
+ start,
175
+ end,
176
+ text: edit.text
155
177
  };
156
178
  }
179
+ #isInsideSurrogatePair(offset) {
180
+ const previous = this.#pieceTable.charAt(offset - 1).charCodeAt(0);
181
+ const next = this.#pieceTable.charAt(offset).charCodeAt(0);
182
+ return previous >= 55296 && previous <= 56319 && next >= 56320 && next <= 57343;
183
+ }
157
184
  #sortAndValidateResolvedEdits(edits) {
158
- const sortedEdits = [...edits].sort((a, b) => a.start - b.start);
185
+ const sortedEdits = [...edits].sort((a, b) => {
186
+ const startDelta = a.start - b.start;
187
+ return startDelta === 0 ? a.end - b.end : startDelta;
188
+ });
159
189
  for (let i = 0; i < sortedEdits.length - 1; i++) if (sortedEdits[i].end > sortedEdits[i + 1].start) throw new Error("Overlapping text edits are not supported");
160
190
  return sortedEdits;
161
191
  }
@@ -164,12 +194,16 @@ var TextDocument = class {
164
194
  const editPositions = this.positionsAt(edits.flatMap((edit) => [edit.start, edit.end]));
165
195
  const changedLineRange = this.#computeChangedLineRange(edits, editPositions);
166
196
  const startPosition = editPositions[0];
197
+ const endPosition = editPositions[editPositions.length - 1];
198
+ const endedAtDocumentEnd = endPosition.line === previousLineCount - 1 && endPosition.character === this.#pieceTable.getLineLength(endPosition.line);
167
199
  this.#pieceTable.applyEdits(edits);
168
200
  const lineCount = this.#pieceTable.lineCount;
169
201
  return {
170
202
  startLine: changedLineRange.startLine,
171
203
  startCharacter: startPosition.character,
204
+ endCharacter: endPosition.character,
172
205
  endLine: Math.min(changedLineRange.endLine, Math.max(0, lineCount - 1)),
206
+ endedAtDocumentEnd,
173
207
  previousLineCount,
174
208
  lineCount,
175
209
  lineDelta: lineCount - previousLineCount,
@@ -183,6 +217,8 @@ var TextDocument = class {
183
217
  let lineDeltaBeforeEdit = 0;
184
218
  const ranges = [];
185
219
  const changes = [];
220
+ const previousLastLine = this.#pieceTable.lineCount - 1;
221
+ const previousLastLineLength = this.#pieceTable.getLineLength(previousLastLine);
186
222
  for (let i = 0; i < edits.length; i++) {
187
223
  const edit = edits[i];
188
224
  const editStart = editPositions[i * 2];
@@ -201,7 +237,10 @@ var TextDocument = class {
201
237
  changes.push([
202
238
  changedStartLine,
203
239
  changedEndLine,
204
- lineDelta
240
+ lineDelta,
241
+ editStart.character,
242
+ editEnd.character,
243
+ editEndLine === previousLastLine && editEnd.character === previousLastLineLength
205
244
  ]);
206
245
  lineDeltaBeforeEdit += lineDelta;
207
246
  }
@@ -212,7 +251,10 @@ var TextDocument = class {
212
251
  changes: [[
213
252
  0,
214
253
  0,
215
- 0
254
+ 0,
255
+ 0,
256
+ 0,
257
+ false
216
258
  ]]
217
259
  };
218
260
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"textDocument.js","names":["#uri","#languageId","#version","#pieceTable","#editStack","#resolveEdit","#sortAndValidateResolvedEdits","#applyResolvedEditsToBuffer","#computeChangedLineRange"],"sources":["../../src/editor/textDocument.ts"],"sourcesContent":["import type { DiffLineAnnotation } from '../types';\nimport { countLineBreaks } from '../utils/computeFileOffsets';\nimport {\n coalesceEditStackEntries,\n createEditStackEntry,\n EditStack,\n shouldCoalesceEditStackEntry,\n} from './editStack';\nimport { PieceTable } from './pieceTable';\nimport type { SearchParams } from './searchPanel';\nimport { type EditorSelection } from './selection';\n\n/**\n * Position in a text document expressed as zero-based line and character offset.\n * The offsets are based on a UTF-16 string representation. So a string of the form\n * `a𐐀b` the character offset of the character `a` is 0, the character offset of `𐐀`\n * is 1 and the character offset of b is 3 since `𐐀` is represented using two code\n * units in UTF-16.\n *\n * Positions are line end character agnostic. So you can not specify a position that\n * denotes `\\r|\\n` or `\\n|` where `|` represents the character offset.\n */\nexport interface Position {\n /**\n * Line position in a document (zero-based).\n *\n * If a line number is greater than the number of lines in a document, it\n * defaults back to the number of lines in the document.\n * If a line number is negative, it defaults to 0.\n *\n * The above two properties are implementation specific.\n */\n readonly line: number;\n /**\n * Character offset on a line in a document (zero-based).\n *\n * The meaning of this offset is determined by the negotiated\n * `PositionEncodingKind`.\n *\n * If the character value is greater than the line length it defaults back\n * to the line length. This property is implementation specific.\n */\n readonly character: number;\n}\n\n/**\n * A range in a text document expressed as (zero-based) start and end positions.\n *\n * If you want to specify a range that contains a line including the line ending\n * character(s) then use an end position denoting the start of the next line.\n * For example:\n * ```ts\n * {\n * start: { line: 5, character: 23 }\n * end : { line 6, character : 0 }\n * }\n * ```\n */\nexport interface Range {\n /**\n * The range's start position.\n */\n readonly start: Position;\n /**\n * The range's end position.\n */\n readonly end: Position;\n}\n\n/**\n * A text edit applicable to a text document.\n */\nexport interface TextEdit {\n /**\n * The range of the text document to be manipulated. To insert\n * text into a document create a range where start === end.\n */\n readonly range: Range;\n /**\n * The string to be inserted. For delete operations use an\n * empty string.\n */\n readonly newText: string;\n}\n\n/** Different with `TextEdit`, the range has been resolved to offsets. */\nexport interface ResolvedTextEdit {\n /** The start offset of the text change. */\n readonly start: number;\n /** The end offset of the text change. */\n readonly end: number;\n /**\n * The string to be inserted. For delete operations use an\n * empty string.\n */\n readonly text: string;\n}\n\nexport interface TextDocumentChange {\n /** First line whose rendered content or tokenizer state may have changed. */\n readonly startLine: number;\n /** Character on the first changed line where the edit began. */\n readonly startCharacter: number;\n /** Last line whose rendered content may have changed after the edit. */\n readonly endLine: number;\n /** Line count before the edit was applied. */\n readonly previousLineCount: number;\n /** Line count after the edit was applied. */\n readonly lineCount: number;\n /** Difference between the old and new line counts. */\n readonly lineDelta: number;\n /** Exact rendered line ranges touched by each edit after the edit was applied. */\n readonly changedLineRanges: readonly [startLine: number, endLine: number][];\n /** Per-edit rendered line ranges before adjacent ranges are coalesced. */\n readonly changedLineChanges?: readonly [\n startLine: number,\n endLine: number,\n lineDelta: number,\n ][];\n}\n\n/**\n * A vscode-languageserver-textdocument compatible text document.\n */\nexport class TextDocument<LAnnotation> {\n #uri: string;\n #languageId: string;\n #version: number;\n #pieceTable: PieceTable;\n #editStack: EditStack<LAnnotation>;\n\n constructor(\n uri: string,\n text: string,\n languageId = 'plaintext',\n version = 0,\n editStack: EditStack<LAnnotation> = new EditStack()\n ) {\n this.#uri = new URL(uri, 'file://').toString();\n this.#languageId = languageId;\n this.#version = version;\n this.#pieceTable = new PieceTable(text);\n this.#editStack = editStack;\n }\n\n get uri(): string {\n return this.#uri;\n }\n\n get languageId(): string {\n return this.#languageId;\n }\n\n get version(): number {\n return this.#version;\n }\n\n get lineCount(): number {\n return this.#pieceTable.lineCount;\n }\n\n // The line ending the document uses, detected from its first line. Lets\n // inserted or pasted text match the rest of the file instead of leaving\n // mixed endings behind. Recognizes Windows `\\r\\n` and classic-Mac lone `\\r`\n // breaks (both of which the piece table already splits on), defaulting to\n // Unix `\\n`.\n get eol(): string {\n if (this.lineCount > 1) {\n const firstLineBreak = this.getLineText(0, true);\n if (firstLineBreak.endsWith('\\r\\n')) {\n return '\\r\\n';\n }\n if (firstLineBreak.endsWith('\\r')) {\n return '\\r';\n }\n }\n return '\\n';\n }\n\n get canUndo(): boolean {\n return this.#editStack.canUndo;\n }\n\n get canRedo(): boolean {\n return this.#editStack.canRedo;\n }\n\n positionAt(offset: number): Position {\n return this.#pieceTable.positionAt(offset);\n }\n\n positionsAt(offsets: readonly number[]): Position[] {\n return this.#pieceTable.positionsAt(offsets);\n }\n\n offsetAt(position: Position): number {\n return this.#pieceTable.offsetAt(this.normalizePosition(position));\n }\n\n getText(range?: Range): string {\n if (range === undefined) {\n return this.#pieceTable.getText();\n }\n // Clamp the range to visible line content before extracting text. A\n // preserved vertical-move \"goal column\" can leave a selection focus whose\n // character overshoots a shorter line; without this, the piece table clamps\n // to the line's offset span (which includes the trailing line break) and\n // copy/cut would pull in that newline. Mirrors `offsetAt`, which normalizes\n // positions the same way.\n return this.#pieceTable.getText({\n start: this.normalizePosition(range.start),\n end: this.normalizePosition(range.end),\n });\n }\n\n getLineText(line: number, includeLineBreak?: boolean): string {\n return this.#pieceTable.getLineText(line, includeLineBreak);\n }\n\n // Rewrites every line break in `text` to the document's EOL. Clipboard text\n // can carry Windows (`\\r\\n`) or classic-Mac (`\\r`) breaks; inserting it\n // verbatim would leave mixed line endings in a file that uses one style.\n normalizeEol(text: string): string {\n return text.replace(/\\r\\n|\\r|\\n/g, this.eol);\n }\n\n getLineLength(line: number, includeLineBreak?: boolean): number {\n return this.#pieceTable.getLineLength(line, includeLineBreak);\n }\n\n charAt(offset: number): string;\n charAt(position: Position): string;\n charAt(positionOrOffset: Position | number): string {\n if (typeof positionOrOffset === 'number') {\n return this.#pieceTable.charAt(positionOrOffset);\n }\n return this.#pieceTable.charAt(this.offsetAt(positionOrOffset));\n }\n\n getTextSlice(start: number, end: number): string {\n return this.#pieceTable.getTextSlice(start, end);\n }\n\n findNextNonOverlappingSubstring(\n needle: string,\n occupied: readonly [start: number, end: number][]\n ): number | undefined {\n return this.#pieceTable.findNextNonOverlappingSubstring(needle, occupied);\n }\n\n search(searchParams: SearchParams): [start: number, end: number][] {\n return this.#pieceTable.search(searchParams);\n }\n\n applyEdits(\n edits: TextEdit[],\n updateHistory = false,\n selectionsBefore?: EditorSelection[],\n selectionsAfter?: EditorSelection[],\n undoBoundary = false\n ): TextDocumentChange | undefined {\n if (edits.length === 0) {\n return;\n }\n return this.applyResolvedEdits(\n edits.map((edit) => this.#resolveEdit(edit)),\n updateHistory,\n selectionsBefore,\n selectionsAfter,\n undoBoundary\n );\n }\n\n applyResolvedEdits(\n edits: ResolvedTextEdit[],\n updateHistory = false,\n selectionsBefore?: EditorSelection[],\n selectionsAfter?: EditorSelection[],\n undoBoundary = false\n ): TextDocumentChange | undefined {\n if (edits.length === 0) {\n return undefined;\n }\n const resolvedEdits = this.#sortAndValidateResolvedEdits(edits);\n if (updateHistory) {\n const entry = createEditStackEntry(\n this,\n resolvedEdits,\n this.#version,\n this.#version + 1,\n selectionsBefore,\n selectionsAfter\n );\n if (undoBoundary) {\n entry.undoBoundary = true;\n }\n const previousEntry = this.#editStack.peekUndo();\n const change = this.#applyResolvedEditsToBuffer(resolvedEdits);\n this.#version++;\n if (\n change.lineDelta === 0 &&\n shouldCoalesceEditStackEntry(previousEntry, entry)\n ) {\n this.#editStack.replaceLastUndo(\n coalesceEditStackEntries(previousEntry!, entry)\n );\n } else {\n this.#editStack.push(entry);\n }\n return change;\n }\n const change = this.#applyResolvedEditsToBuffer(resolvedEdits);\n this.#version++;\n return change;\n }\n\n setLastUndoSelectionsAfter(selections: EditorSelection[]): void {\n this.#editStack.setLastUndoSelectionsAfter(selections);\n }\n\n setLastUndoLineAnnotations(\n lineAnnotationsBefore: DiffLineAnnotation<LAnnotation>[],\n lineAnnotationsAfter: DiffLineAnnotation<LAnnotation>[]\n ): void {\n this.#editStack.setLastUndoLineAnnotations(\n lineAnnotationsBefore,\n lineAnnotationsAfter\n );\n }\n\n undo():\n | [\n change: TextDocumentChange,\n selections?: EditorSelection[],\n lineAnnotations?: DiffLineAnnotation<LAnnotation>[],\n ]\n | undefined {\n const entry = this.#editStack.popUndoToRedo();\n if (entry === undefined) {\n return undefined;\n }\n const change = this.#applyResolvedEditsToBuffer(entry.inverseEdits);\n if (change === undefined) {\n return undefined;\n }\n this.#version = entry.versionBefore;\n return [\n change,\n entry.selectionsBefore?.slice(),\n entry.lineAnnotationsBefore?.slice(),\n ];\n }\n\n redo():\n | [\n change: TextDocumentChange,\n selections?: EditorSelection[],\n lineAnnotations?: DiffLineAnnotation<LAnnotation>[],\n ]\n | undefined {\n const entry = this.#editStack.popRedoToUndo();\n if (entry === undefined) {\n return undefined;\n }\n const change = this.#applyResolvedEditsToBuffer(entry.forwardEdits);\n if (change === undefined) {\n return undefined;\n }\n this.#version = entry.versionAfter;\n return [\n change,\n entry.selectionsAfter?.slice(),\n entry.lineAnnotationsAfter?.slice(),\n ];\n }\n\n normalizePosition(position: Position): Position {\n const line = Math.max(0, Math.min(position.line, this.lineCount - 1));\n return {\n line,\n character: Math.max(\n 0,\n Math.min(position.character, this.getLineLength(line))\n ),\n };\n }\n\n #resolveEdit(edit: TextEdit): ResolvedTextEdit {\n let start = this.offsetAt(edit.range.start);\n let end = this.offsetAt(edit.range.end);\n if (start > end) {\n const t = start;\n start = end;\n end = t;\n }\n return { start, end, text: edit.newText };\n }\n\n #sortAndValidateResolvedEdits(edits: ResolvedTextEdit[]): ResolvedTextEdit[] {\n const sortedEdits = [...edits].sort((a, b) => a.start - b.start);\n for (let i = 0; i < sortedEdits.length - 1; i++) {\n if (sortedEdits[i].end > sortedEdits[i + 1].start) {\n throw new Error('Overlapping text edits are not supported');\n }\n }\n return sortedEdits;\n }\n\n #applyResolvedEditsToBuffer(edits: ResolvedTextEdit[]): TextDocumentChange {\n const previousLineCount = this.#pieceTable.lineCount;\n const editPositions = this.positionsAt(\n edits.flatMap((edit) => [edit.start, edit.end])\n );\n const changedLineRange = this.#computeChangedLineRange(\n edits,\n editPositions\n );\n const startPosition = editPositions[0];\n this.#pieceTable.applyEdits(edits);\n const lineCount = this.#pieceTable.lineCount;\n const change: TextDocumentChange = {\n startLine: changedLineRange.startLine,\n startCharacter: startPosition.character,\n endLine: Math.min(changedLineRange.endLine, Math.max(0, lineCount - 1)),\n previousLineCount,\n lineCount,\n lineDelta: lineCount - previousLineCount,\n changedLineRanges: changedLineRange.ranges,\n changedLineChanges: changedLineRange.changes,\n };\n return change;\n }\n\n #computeChangedLineRange(\n edits: ResolvedTextEdit[],\n editPositions: Position[]\n ): {\n startLine: number;\n endLine: number;\n ranges: [number, number][];\n changes: [startLine: number, endLine: number, lineDelta: number][];\n } {\n let startLine = Infinity;\n let endLine = 0;\n let lineDeltaBeforeEdit = 0;\n const ranges: [number, number][] = [];\n const changes: [startLine: number, endLine: number, lineDelta: number][] =\n [];\n for (let i = 0; i < edits.length; i++) {\n const edit = edits[i];\n const editStart = editPositions[i * 2];\n const editEnd = editPositions[i * 2 + 1];\n const editStartLine = editStart.line;\n const editEndLine = editEnd.line;\n const insertedLineSpan = countLineBreaks(edit.text);\n const changedStartLine = editStartLine + lineDeltaBeforeEdit;\n const changedEndLine = changedStartLine + insertedLineSpan;\n const lineDelta = insertedLineSpan - (editEndLine - editStartLine);\n startLine = Math.min(startLine, editStartLine);\n endLine = Math.max(endLine, changedEndLine);\n const lastRange = ranges[ranges.length - 1];\n if (lastRange !== undefined && changedStartLine <= lastRange[1] + 1) {\n ranges[ranges.length - 1] = [\n lastRange[0],\n Math.max(lastRange[1], changedEndLine),\n ];\n } else {\n ranges.push([changedStartLine, changedEndLine]);\n }\n changes.push([changedStartLine, changedEndLine, lineDelta]);\n lineDeltaBeforeEdit += lineDelta;\n }\n if (startLine === Infinity) {\n return {\n startLine: 0,\n endLine: 0,\n ranges: [[0, 0]],\n changes: [[0, 0, 0]],\n };\n }\n return { startLine, endLine, ranges, changes };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"textDocument.js","names":["#uri","#languageId","#version","#pieceTable","#editStack","#eol","#applyResolvedEdits","#sortAndValidateResolvedEdits","#resolveEdit","#normalizeResolvedEdit","#applyResolvedEditsToBuffer","#clampIndex","#isInsideSurrogatePair","#computeChangedLineRange"],"sources":["../../src/editor/textDocument.ts"],"sourcesContent":["import type {\n DiffLineAnnotation,\n EditorSelection,\n Position,\n Range,\n TextEdit,\n} from '../types';\nimport { countLineBreaks } from '../utils/computeFileOffsets';\nimport {\n coalesceEditStackEntries,\n createEditStackEntry,\n EditStack,\n shouldCoalesceEditStackEntry,\n} from './editStack';\nimport { PieceTable } from './pieceTable';\nimport type { SearchParams } from './searchPanel';\n\nexport type { Position, Range, TextEdit } from '../types';\n\n/** Different with `TextEdit`, the range has been resolved to offsets. */\nexport interface ResolvedTextEdit {\n /** The start offset of the text change. */\n readonly start: number;\n /** The end offset of the text change. */\n readonly end: number;\n /**\n * The string to be inserted. For delete operations use an\n * empty string.\n */\n readonly text: string;\n}\n\nexport interface TextDocumentChange {\n /** First line whose rendered content or tokenizer state may have changed. */\n readonly startLine: number;\n /** Character on the first changed line where the edit began. */\n readonly startCharacter: number;\n /** Character on the original last changed line where the edit ended. */\n readonly endCharacter: number;\n /** Last line whose rendered content may have changed after the edit. */\n readonly endLine: number;\n /** Whether the original edit range ended at the previous document EOF. */\n readonly endedAtDocumentEnd: boolean;\n /** Line count before the edit was applied. */\n readonly previousLineCount: number;\n /** Line count after the edit was applied. */\n readonly lineCount: number;\n /** Difference between the old and new line counts. */\n readonly lineDelta: number;\n /** Exact rendered line ranges touched by each edit after the edit was applied. */\n readonly changedLineRanges: readonly [startLine: number, endLine: number][];\n /** Per-edit rendered line ranges before adjacent ranges are coalesced. */\n readonly changedLineChanges?: readonly [\n startLine: number,\n endLine: number,\n lineDelta: number,\n startCharacter?: number,\n endCharacter?: number,\n endedAtDocumentEnd?: boolean,\n ][];\n}\n\n// Metadata-less replay results include the resolved edits so Editor can remap\n// its live selections without storing a snapshot on the history entry.\ntype TextDocumentHistoryResult<LAnnotation> = [\n change: TextDocumentChange,\n selections?: EditorSelection[],\n lineAnnotations?: DiffLineAnnotation<LAnnotation>[],\n selectionEdits?: ResolvedTextEdit[],\n];\n\n/**\n * A vscode-languageserver-textdocument compatible text document.\n */\nexport class TextDocument<LAnnotation> {\n #uri: string;\n #languageId: string;\n #version: number;\n #pieceTable: PieceTable;\n #editStack: EditStack<LAnnotation>;\n #eol: string;\n\n constructor(\n uri: string,\n text: string,\n languageId = 'text',\n version = 0,\n editStack: EditStack<LAnnotation> = new EditStack()\n ) {\n this.#uri = new URL(uri, 'file://').toString();\n this.#languageId = languageId;\n this.#version = version;\n this.#pieceTable = new PieceTable(text);\n this.#editStack = editStack;\n\n // The line ending the document uses, detected once from its first line. Lets\n // inserted or pasted text match the rest of the file instead of leaving\n // mixed endings behind and keeps that convention stable as the file is\n // edited. Defaults to Unix `\\n` when the initial text has no line break.\n const firstLineBreak = this.#pieceTable.getLineText(0, true);\n if (firstLineBreak.endsWith('\\r\\n')) {\n this.#eol = '\\r\\n';\n } else if (firstLineBreak.endsWith('\\r')) {\n this.#eol = '\\r';\n } else {\n this.#eol = '\\n';\n }\n }\n\n get uri(): string {\n return this.#uri;\n }\n\n get languageId(): string {\n return this.#languageId;\n }\n\n get version(): number {\n return this.#version;\n }\n\n get lineCount(): number {\n return this.#pieceTable.lineCount;\n }\n\n get eol(): string {\n return this.#eol;\n }\n\n get canUndo(): boolean {\n return this.#editStack.canUndo;\n }\n\n get canRedo(): boolean {\n return this.#editStack.canRedo;\n }\n\n positionAt(offset: number): Position {\n return this.normalizePosition(this.#pieceTable.positionAt(offset));\n }\n\n positionsAt(offsets: readonly number[]): Position[] {\n const positions = this.#pieceTable.positionsAt(offsets);\n for (let i = 0; i < positions.length; i++) {\n positions[i] = this.normalizePosition(positions[i]);\n }\n return positions;\n }\n\n offsetAt(position: Position): number {\n return this.#pieceTable.offsetAt(this.normalizePosition(position));\n }\n\n getText(range?: Range): string {\n if (range === undefined) {\n return this.#pieceTable.getText();\n }\n // Clamp the range to visible line content before extracting text. A\n // preserved vertical-move \"goal column\" can leave a selection focus whose\n // character overshoots a shorter line; without this, the piece table clamps\n // to the line's offset span (which includes the trailing line break) and\n // copy/cut would pull in that newline. Mirrors `offsetAt`, which normalizes\n // positions the same way.\n return this.#pieceTable.getText({\n start: this.normalizePosition(range.start),\n end: this.normalizePosition(range.end),\n });\n }\n\n getLineText(line: number, includeLineBreak?: boolean): string {\n return this.#pieceTable.getLineText(line, includeLineBreak);\n }\n\n // Rewrites every line break in `text` to the document's EOL. Clipboard text\n // can carry Windows (`\\r\\n`) or classic-Mac (`\\r`) breaks; inserting it\n // verbatim would leave mixed line endings in a file that uses one style.\n normalizeEol(text: string): string {\n return text.replace(/\\r\\n|\\r|\\n/g, this.eol);\n }\n\n getLineLength(line: number, includeLineBreak?: boolean): number {\n return this.#pieceTable.getLineLength(line, includeLineBreak);\n }\n\n charAt(offset: number): string;\n charAt(position: Position): string;\n charAt(positionOrOffset: Position | number): string {\n if (typeof positionOrOffset === 'number') {\n return this.#pieceTable.charAt(positionOrOffset);\n }\n return this.#pieceTable.charAt(this.offsetAt(positionOrOffset));\n }\n\n getTextSlice(start: number, end: number): string {\n return this.#pieceTable.getTextSlice(start, end);\n }\n\n findNextNonOverlappingSubstring(\n needle: string,\n occupied: readonly [start: number, end: number][]\n ): number | undefined {\n return this.#pieceTable.findNextNonOverlappingSubstring(needle, occupied);\n }\n\n search(searchParams: SearchParams): [start: number, end: number][] {\n return this.#pieceTable.search(searchParams);\n }\n\n applyEdits(\n edits: TextEdit[],\n updateHistory = true,\n selectionsBefore?: EditorSelection[],\n selectionsAfter?: EditorSelection[],\n undoBoundary = false\n ): TextDocumentChange | undefined {\n if (edits.length === 0) {\n return;\n }\n return this.#applyResolvedEdits(\n this.#sortAndValidateResolvedEdits(this.resolveEdits(edits)),\n updateHistory,\n selectionsBefore,\n selectionsAfter,\n undoBoundary\n );\n }\n\n // Converts line/character ranges to the exact UTF-16 offsets applyEdits\n // uses, including widening invalid boundaries so they cannot split a valid\n // surrogate pair. Editor caret remapping uses the same resolved geometry.\n resolveEdits(edits: readonly TextEdit[]): ResolvedTextEdit[] {\n return edits.map((edit) => this.#resolveEdit(edit));\n }\n\n applyResolvedEdits(\n edits: ResolvedTextEdit[],\n updateHistory = true,\n selectionsBefore?: EditorSelection[],\n selectionsAfter?: EditorSelection[],\n undoBoundary = false\n ): TextDocumentChange | undefined {\n if (edits.length === 0) {\n return undefined;\n }\n return this.#applyResolvedEdits(\n this.#sortAndValidateResolvedEdits(\n edits.map((edit) => this.#normalizeResolvedEdit(edit))\n ),\n updateHistory,\n selectionsBefore,\n selectionsAfter,\n undoBoundary\n );\n }\n\n // Every edit joins the undo timeline: an edit applied with\n // `updateHistory=false` affects the edit stack exactly as the identical\n // call with `updateHistory=true` and no selections or undo boundary would.\n // The flag only controls whether the caller's interaction metadata is\n // recorded on the entry; the entry itself is always pushed (clearing any\n // pending redo) and coalesces by the normal geometry rules. This keeps a\n // mixed tracked/untracked sequence equivalent to the all-tracked sequence —\n // undo-to-exhaustion restores the original text, redo-to-exhaustion the\n // final text — instead of leaving frozen entries whose offsets go stale.\n // Only history replay (undo/redo) writes to the buffer without recording.\n #applyResolvedEdits(\n resolvedEdits: ResolvedTextEdit[],\n updateHistory: boolean,\n selectionsBefore: EditorSelection[] | undefined,\n selectionsAfter: EditorSelection[] | undefined,\n undoBoundary: boolean\n ): TextDocumentChange {\n const entry = createEditStackEntry(\n this,\n resolvedEdits,\n this.#version,\n this.#version + 1,\n updateHistory ? selectionsBefore : undefined,\n updateHistory ? selectionsAfter : undefined\n );\n if (updateHistory && undoBoundary) {\n entry.undoBoundary = true;\n }\n const previousEntry = this.#editStack.peekUndoForCoalescing();\n const change = this.#applyResolvedEditsToBuffer(resolvedEdits);\n this.#version++;\n if (\n change.lineDelta === 0 &&\n shouldCoalesceEditStackEntry(previousEntry, entry)\n ) {\n this.#editStack.replaceLastUndo(\n coalesceEditStackEntries(previousEntry!, entry)\n );\n } else {\n this.#editStack.push(entry);\n }\n return change;\n }\n\n setLastUndoSelectionsAfter(selections: EditorSelection[]): void {\n this.#editStack.setLastUndoSelectionsAfter(selections);\n }\n\n setLastUndoLineAnnotations(\n lineAnnotationsBefore: DiffLineAnnotation<LAnnotation>[],\n lineAnnotationsAfter: DiffLineAnnotation<LAnnotation>[]\n ): void {\n this.#editStack.setLastUndoLineAnnotations(\n lineAnnotationsBefore,\n lineAnnotationsAfter\n );\n }\n\n undo(): TextDocumentHistoryResult<LAnnotation> | undefined {\n const entry = this.#editStack.popUndoToRedo();\n if (entry === undefined) {\n return undefined;\n }\n const change = this.#applyResolvedEditsToBuffer(entry.inverseEdits);\n if (change === undefined) {\n return undefined;\n }\n this.#version = entry.versionBefore;\n const selections = entry.selectionsBefore?.slice();\n return [\n change,\n selections,\n entry.lineAnnotationsBefore?.slice(),\n selections === undefined\n ? entry.inverseEdits.map((edit) => ({ ...edit }))\n : undefined,\n ];\n }\n\n redo(): TextDocumentHistoryResult<LAnnotation> | undefined {\n const entry = this.#editStack.popRedoToUndo();\n if (entry === undefined) {\n return undefined;\n }\n const change = this.#applyResolvedEditsToBuffer(entry.forwardEdits);\n if (change === undefined) {\n return undefined;\n }\n this.#version = entry.versionAfter;\n const selections = entry.selectionsAfter?.slice();\n return [\n change,\n selections,\n entry.lineAnnotationsAfter?.slice(),\n selections === undefined\n ? entry.forwardEdits.map((edit) => ({ ...edit }))\n : undefined,\n ];\n }\n\n normalizePosition(position: Position): Position {\n const line = TextDocument.#clampIndex(position.line, this.lineCount - 1);\n return {\n line,\n character: TextDocument.#clampIndex(\n position.character,\n this.getLineLength(line)\n ),\n };\n }\n\n // Math.min/max pass NaN through, and a fractional index breaks the\n // integer-keyed line-offset lookup — either way the resolved offset becomes\n // NaN and a degenerate edit range can swallow the whole document. Malformed\n // components clamp like any other out-of-range value: NaN and -Infinity act\n // as 0, fractions floor, +Infinity clamps to the max.\n static #clampIndex(value: number, max: number): number {\n if (Number.isNaN(value)) {\n return 0;\n }\n return Math.max(0, Math.min(Math.floor(value), max));\n }\n\n #resolveEdit(edit: TextEdit): ResolvedTextEdit {\n let start = this.offsetAt(edit.range.start);\n let end = this.offsetAt(edit.range.end);\n if (start > end) {\n const t = start;\n start = end;\n end = t;\n }\n return this.#normalizeResolvedEdit({\n start,\n end,\n text: edit.newText,\n });\n }\n\n // Snaps an insertion before a pair and widens replacement boundaries\n // outward, so every edit addresses whole UTF-16 surrogate pairs.\n #normalizeResolvedEdit(edit: ResolvedTextEdit): ResolvedTextEdit {\n let { start, end } = edit;\n const isInsertion = start === end;\n if (this.#isInsideSurrogatePair(start)) {\n start--;\n }\n if (isInsertion) {\n end = start;\n } else if (this.#isInsideSurrogatePair(end)) {\n end++;\n }\n return { start, end, text: edit.text };\n }\n\n // A UTF-16 offset is invalid when it sits between the high and low units of\n // one well-formed surrogate pair. Lone surrogate units remain addressable.\n #isInsideSurrogatePair(offset: number): boolean {\n const previous = this.#pieceTable.charAt(offset - 1).charCodeAt(0);\n const next = this.#pieceTable.charAt(offset).charCodeAt(0);\n return (\n previous >= 0xd800 &&\n previous <= 0xdbff &&\n next >= 0xdc00 &&\n next <= 0xdfff\n );\n }\n\n #sortAndValidateResolvedEdits(edits: ResolvedTextEdit[]): ResolvedTextEdit[] {\n // Put zero-width edits before ranges at the same start so validation and\n // application do not depend on the caller's batch order.\n const sortedEdits = [...edits].sort((a, b) => {\n const startDelta = a.start - b.start;\n return startDelta === 0 ? a.end - b.end : startDelta;\n });\n for (let i = 0; i < sortedEdits.length - 1; i++) {\n if (sortedEdits[i].end > sortedEdits[i + 1].start) {\n throw new Error('Overlapping text edits are not supported');\n }\n }\n return sortedEdits;\n }\n\n #applyResolvedEditsToBuffer(edits: ResolvedTextEdit[]): TextDocumentChange {\n const previousLineCount = this.#pieceTable.lineCount;\n const editPositions = this.positionsAt(\n edits.flatMap((edit) => [edit.start, edit.end])\n );\n const changedLineRange = this.#computeChangedLineRange(\n edits,\n editPositions\n );\n const startPosition = editPositions[0];\n const endPosition = editPositions[editPositions.length - 1];\n const endedAtDocumentEnd =\n endPosition.line === previousLineCount - 1 &&\n endPosition.character ===\n this.#pieceTable.getLineLength(endPosition.line);\n this.#pieceTable.applyEdits(edits);\n const lineCount = this.#pieceTable.lineCount;\n const change: TextDocumentChange = {\n startLine: changedLineRange.startLine,\n startCharacter: startPosition.character,\n endCharacter: endPosition.character,\n endLine: Math.min(changedLineRange.endLine, Math.max(0, lineCount - 1)),\n endedAtDocumentEnd,\n previousLineCount,\n lineCount,\n lineDelta: lineCount - previousLineCount,\n changedLineRanges: changedLineRange.ranges,\n changedLineChanges: changedLineRange.changes,\n };\n return change;\n }\n\n #computeChangedLineRange(\n edits: ResolvedTextEdit[],\n editPositions: Position[]\n ): {\n startLine: number;\n endLine: number;\n ranges: [number, number][];\n changes: [\n startLine: number,\n endLine: number,\n lineDelta: number,\n startCharacter: number,\n endCharacter: number,\n endedAtDocumentEnd: boolean,\n ][];\n } {\n let startLine = Infinity;\n let endLine = 0;\n let lineDeltaBeforeEdit = 0;\n const ranges: [number, number][] = [];\n const changes: [\n startLine: number,\n endLine: number,\n lineDelta: number,\n startCharacter: number,\n endCharacter: number,\n endedAtDocumentEnd: boolean,\n ][] = [];\n const previousLastLine = this.#pieceTable.lineCount - 1;\n const previousLastLineLength =\n this.#pieceTable.getLineLength(previousLastLine);\n for (let i = 0; i < edits.length; i++) {\n const edit = edits[i];\n const editStart = editPositions[i * 2];\n const editEnd = editPositions[i * 2 + 1];\n const editStartLine = editStart.line;\n const editEndLine = editEnd.line;\n const insertedLineSpan = countLineBreaks(edit.text);\n const changedStartLine = editStartLine + lineDeltaBeforeEdit;\n const changedEndLine = changedStartLine + insertedLineSpan;\n const lineDelta = insertedLineSpan - (editEndLine - editStartLine);\n startLine = Math.min(startLine, editStartLine);\n endLine = Math.max(endLine, changedEndLine);\n const lastRange = ranges[ranges.length - 1];\n if (lastRange !== undefined && changedStartLine <= lastRange[1] + 1) {\n ranges[ranges.length - 1] = [\n lastRange[0],\n Math.max(lastRange[1], changedEndLine),\n ];\n } else {\n ranges.push([changedStartLine, changedEndLine]);\n }\n changes.push([\n changedStartLine,\n changedEndLine,\n lineDelta,\n editStart.character,\n editEnd.character,\n editEndLine === previousLastLine &&\n editEnd.character === previousLastLineLength,\n ]);\n lineDeltaBeforeEdit += lineDelta;\n }\n if (startLine === Infinity) {\n return {\n startLine: 0,\n endLine: 0,\n ranges: [[0, 0]],\n changes: [[0, 0, 0, 0, 0, false]],\n };\n }\n return { startLine, endLine, ranges, changes };\n }\n}\n"],"mappings":"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"}
@@ -13,15 +13,18 @@ declare class Metrics {
13
13
  init(root: HTMLElement): void;
14
14
  /**
15
15
  * Re-measure the '0' character width against the font that is loaded right
16
- * now, returning true when it changed.
16
+ * now, returning true when measurement-dependent UI should refresh.
17
17
  *
18
18
  * A custom web font can finish loading after the editor first renders.
19
19
  * Until then canvas measureText reports the fallback font's width, and
20
20
  * getComputedStyle returns the same font-family string before and after the
21
21
  * file arrives, so init()'s font guard never re-measures on its own. Call
22
22
  * this once fonts have settled (e.g. on document.fonts.ready) to replace a
23
- * width measured against the fallback font with the real glyph width. The
24
- * boolean return lets the caller skip re-rendering when nothing changed.
23
+ * width measured against the fallback font with the real glyph width. Font
24
+ * completion can also change DOM-measured emoji/ZWJ widths while leaving the
25
+ * ASCII width unchanged, and the computed font string stays the same in both
26
+ * cases. The boolean return lets the caller skip re-rendering when there is no
27
+ * measured state to refresh.
25
28
  */
26
29
  remeasureCharacterWidth(): boolean;
27
30
  /** measure the width of the text */
@@ -47,14 +50,6 @@ declare function needsDomTextMeasurement(text: string): boolean;
47
50
  declare function snapTextOffsetToUnicodeBoundary(text: string, offset: number): number;
48
51
  /** get the offsets of the Unicode grapheme clusters in the text */
49
52
  declare function getUnicodeMeasurementOffsets(text: string): number[] | undefined;
50
- /**
51
- * Expand tab characters to spaces using fixed tab stops: each tab advances to
52
- * the next multiple of tabSize from its running column, matching how the
53
- * rendered text expands tabs via CSS `tab-size`. Expanding every tab to a flat
54
- * tabSize would mis-measure tabs that follow other characters on the same line
55
- * (e.g. an alignment tab in `foo\tbar`).
56
- */
57
- declare function expandTabsToSpaces(text: string, tabSize: number): string;
58
53
  /**
59
54
  * Count the rendered columns of ASCII text, advancing each tab to the next
60
55
  * fixed tab stop (a multiple of tabSize) to match CSS `tab-size`. Returns -1
@@ -62,5 +57,5 @@ declare function expandTabsToSpaces(text: string, tabSize: number): string;
62
57
  */
63
58
  declare function getExpandedAsciiTextColumns(text: string, tabSize: number): number;
64
59
  //#endregion
65
- export { Metrics, expandTabsToSpaces, getExpandedAsciiTextColumns, getUnicodeMeasurementOffsets, needsDomTextMeasurement, snapTextOffsetToUnicodeBoundary };
60
+ export { Metrics, getExpandedAsciiTextColumns, getUnicodeMeasurementOffsets, needsDomTextMeasurement, snapTextOffsetToUnicodeBoundary };
66
61
  //# sourceMappingURL=textMeasure.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"textMeasure.d.ts","names":[],"sources":["../../src/editor/textMeasure.ts"],"mappings":";cAQa,OAAA;EAAA;EAAO;EASlB,EAAA;EASsB;EAPtB,OAAA;EAFA;EAIA,UAAA;EAAA;EAEA,UAAA;EAGA;EAAA,IAAA,CAAK,IAAA,EAAM,WAAW;EAAjB;;;;;;;;;;AAgJc;AAMrB;EA1FE,uBAAA;;EAcA,gBAAA,CAAiB,IAAA;EA4EiC;EAnElD,sBAAA,CAAuB,IAAA;EAmFsB;;;AAE/B;AA6ChB;EAtHE,mBAAA,CAAoB,IAAA;;;AAuHR;AA6Bd;;EAnGE,mBAAA;AAAA;AAmG8D;AAAA,iBA7FhD,uBAAA,CAAwB,IAAY;;iBAgBpC,+BAAA,CACd,IAAA,UACA,MAAc;;iBA6CA,4BAAA,CACd,IAAY;;;;;;;;iBA6BE,kBAAA,CAAmB,IAAA,UAAc,OAAe;;;;;;iBAwBhD,2BAAA,CACd,IAAA,UACA,OAAe"}
1
+ {"version":3,"file":"textMeasure.d.ts","names":[],"sources":["../../src/editor/textMeasure.ts"],"mappings":";cAUa,OAAA;EAAA;EAAO;EASlB,EAAA;EASsB;EAPtB,OAAA;EAFA;EAIA,UAAA;EAAA;EAEA,UAAA;EAGA;EAAA,IAAA,CAAK,IAAA,EAAM,WAAW;EAAjB;;;;;;;;;;AAyLc;AAYrB;;;;EAtIE,uBAAA;EAuJc;EAxId,gBAAA,CAAiB,IAAA;;EA8CjB,sBAAA,CAAuB,IAAA;EA4FT;AA6ChB;;;;EA7HE,mBAAA,CAAoB,IAAA;EAyJN;;;;AAEC;EA1Gf,mBAAA;AAAA;;iBAYc,uBAAA,CAAwB,IAAY;;iBAiBpC,+BAAA,CACd,IAAA,UACA,MAAc;;iBA6CA,4BAAA,CACd,IAAY;;;;;;iBA2BE,2BAAA,CACd,IAAA,UACA,OAAe"}