vzcode 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/dist/assets/{buildWorker-Dsx6rOdK.js → buildWorker-wLkQ0yz1.js} +64 -64
  2. package/dist/assets/{index-BM8tDh6M.css → index-BoFeK4GL.css} +1 -1
  3. package/dist/assets/{index-CpxqGhXj.js → index-DuUjtrt6.js} +150 -150
  4. package/dist/assets/{worker-BeBZGG1n.js → worker-DHxSPM7N.js} +78 -78
  5. package/dist/cli.js +113 -0
  6. package/dist/index.html +2 -2
  7. package/{src/ot.ts → dist/ot.js} +2 -14
  8. package/{src/randomId.ts → dist/randomId.js} +5 -6
  9. package/dist/runCode.js +20 -0
  10. package/dist/server/aiChatHandler/aiEditing.js +89 -0
  11. package/dist/server/aiChatHandler/chatOperations.js +360 -0
  12. package/dist/server/aiChatHandler/errorHandling.js +49 -0
  13. package/dist/server/aiChatHandler/index.js +112 -0
  14. package/dist/server/aiChatHandler/llmStreaming.js +284 -0
  15. package/dist/server/aiChatHandler/validation.js +19 -0
  16. package/dist/server/computeInitialDocument.js +146 -0
  17. package/{src/server/config.ts → dist/server/config.js} +4 -7
  18. package/{src/server/featureFlags.ts → dist/server/featureFlags.js} +0 -3
  19. package/dist/server/generateAIResponse.js +129 -0
  20. package/dist/server/handleAIAssist.js +39 -0
  21. package/dist/server/handleAICopilot.js +81 -0
  22. package/dist/server/index.js +297 -0
  23. package/dist/server/livekit.js +17 -0
  24. package/dist/server/prettier.js +90 -0
  25. package/{src/server/setupEnv.ts → dist/server/setupEnv.js} +1 -2
  26. package/dist/submitOperation.js +14 -0
  27. package/dist/types.js +1 -0
  28. package/dist/utils/fileDiff.js +66 -0
  29. package/package.json +22 -22
  30. package/src/client/AIAssist/AIAssistCodeMirrorKeyMap.ts +0 -42
  31. package/src/client/AIAssist/AIAssistWidget/index.tsx +0 -135
  32. package/src/client/AIAssist/AIAssistWidget/style.scss +0 -47
  33. package/src/client/AIAssist/Spinner.jsx +0 -76
  34. package/src/client/AIAssist/formatFile.ts +0 -27
  35. package/src/client/AIAssist/generateFilesContext.ts +0 -25
  36. package/src/client/AIAssist/index.ts +0 -2
  37. package/src/client/AIAssist/startAIAssist.ts +0 -95
  38. package/src/client/App/index.tsx +0 -153
  39. package/src/client/App/style.scss +0 -82
  40. package/src/client/App/usePending.ts +0 -51
  41. package/src/client/App/useShareDB.ts +0 -161
  42. package/src/client/CodeEditor/Copilot/index.ts +0 -74
  43. package/src/client/CodeEditor/InteractiveWidgets/boolToggler.ts +0 -12
  44. package/src/client/CodeEditor/InteractiveWidgets/colorPicker.ts +0 -68
  45. package/src/client/CodeEditor/InteractiveWidgets/colorsInTextPlugin.ts +0 -174
  46. package/src/client/CodeEditor/InteractiveWidgets/highlightWidgets.ts +0 -79
  47. package/src/client/CodeEditor/InteractiveWidgets/index.ts +0 -44
  48. package/src/client/CodeEditor/InteractiveWidgets/markdownCheckboxToggler.ts +0 -12
  49. package/src/client/CodeEditor/InteractiveWidgets/numberDragger.ts +0 -20
  50. package/src/client/CodeEditor/InteractiveWidgets/rotateWidget.ts +0 -143
  51. package/src/client/CodeEditor/InteractiveWidgets/urlClicker.test.ts +0 -48
  52. package/src/client/CodeEditor/InteractiveWidgets/urlClicker.ts +0 -12
  53. package/src/client/CodeEditor/InteractiveWidgets/vec2Slider.ts +0 -25
  54. package/src/client/CodeEditor/basicSetup.ts +0 -73
  55. package/src/client/CodeEditor/getOrCreateEditor.tsx +0 -615
  56. package/src/client/CodeEditor/index.tsx +0 -202
  57. package/src/client/CodeEditor/json1PresenceBroadcast.ts +0 -41
  58. package/src/client/CodeEditor/json1PresenceDisplay.ts +0 -350
  59. package/src/client/CodeEditor/rainbowBrackets.ts +0 -122
  60. package/src/client/CodeEditor/style.scss +0 -156
  61. package/src/client/CodeEditor/typescriptExtension/worker.ts +0 -32
  62. package/src/client/CodeErrorOverlay/index.tsx +0 -77
  63. package/src/client/CodeErrorOverlay/style.scss +0 -33
  64. package/src/client/Fonts/fonts.ts +0 -8
  65. package/src/client/Icons/AdjustmentSVG.tsx +0 -19
  66. package/src/client/Icons/BugSVG.tsx +0 -20
  67. package/src/client/Icons/CloseSVG.tsx +0 -19
  68. package/src/client/Icons/CssSVG.tsx +0 -16
  69. package/src/client/Icons/DirectoryArrowSVG.tsx +0 -19
  70. package/src/client/Icons/EditSVG.tsx +0 -19
  71. package/src/client/Icons/FileSVG.tsx +0 -19
  72. package/src/client/Icons/FolderSVG.tsx +0 -20
  73. package/src/client/Icons/GearSVG.tsx +0 -16
  74. package/src/client/Icons/HtmlSVG.tsx +0 -16
  75. package/src/client/Icons/JavaScriptSVG.tsx +0 -16
  76. package/src/client/Icons/JsonSVG.tsx +0 -16
  77. package/src/client/Icons/MarkdownSVG.tsx +0 -16
  78. package/src/client/Icons/MicOffSVG.tsx +0 -35
  79. package/src/client/Icons/MicSVG.tsx +0 -25
  80. package/src/client/Icons/NewSVG.tsx +0 -19
  81. package/src/client/Icons/PinSVG.tsx +0 -14
  82. package/src/client/Icons/PlaySVG.tsx +0 -16
  83. package/src/client/Icons/QuestionMarkSVG.tsx +0 -18
  84. package/src/client/Icons/ReactSVG.tsx +0 -16
  85. package/src/client/Icons/SearchSVG.tsx +0 -20
  86. package/src/client/Icons/SparklesSVG.tsx +0 -23
  87. package/src/client/Icons/SplitEditorSVG.tsx +0 -34
  88. package/src/client/Icons/StopSVG.tsx +0 -15
  89. package/src/client/Icons/SvelteSVG.tsx +0 -16
  90. package/src/client/Icons/TrashSVG.tsx +0 -19
  91. package/src/client/Icons/TypeScriptSVG.tsx +0 -16
  92. package/src/client/Icons/index.tsx +0 -26
  93. package/src/client/ImageViewer/index.tsx +0 -91
  94. package/src/client/ImageViewer/style.scss +0 -41
  95. package/src/client/PresenceNotifications/PresenceNotifications.test.ts +0 -134
  96. package/src/client/PresenceNotifications/index.tsx +0 -158
  97. package/src/client/PresenceNotifications/style.scss +0 -17
  98. package/src/client/RunCodeWidget/index.tsx +0 -117
  99. package/src/client/RunCodeWidget/style.scss +0 -24
  100. package/src/client/SplitPaneResizeContext.tsx +0 -159
  101. package/src/client/TabList/Tab.tsx +0 -73
  102. package/src/client/TabList/index.tsx +0 -35
  103. package/src/client/TabList/style.scss +0 -51
  104. package/src/client/VZCodeContext/index.tsx +0 -29
  105. package/src/client/VZCodeContext/types.ts +0 -249
  106. package/src/client/VZCodeContext/useVZCodeState.ts +0 -584
  107. package/src/client/VZKeyboardShortcutsDoc.tsx +0 -148
  108. package/src/client/VZLeft.tsx +0 -28
  109. package/src/client/VZMiddle.tsx +0 -172
  110. package/src/client/VZResizer/index.tsx +0 -128
  111. package/src/client/VZResizer/styles.scss +0 -33
  112. package/src/client/VZRight.tsx +0 -113
  113. package/src/client/VZSettings.tsx +0 -275
  114. package/src/client/VZSidebar/AIChat/ChatInput.tsx +0 -239
  115. package/src/client/VZSidebar/AIChat/DiffView.scss +0 -136
  116. package/src/client/VZSidebar/AIChat/DiffView.tsx +0 -176
  117. package/src/client/VZSidebar/AIChat/Message.tsx +0 -143
  118. package/src/client/VZSidebar/AIChat/MessageList.tsx +0 -205
  119. package/src/client/VZSidebar/AIChat/StreamingMessage.tsx +0 -35
  120. package/src/client/VZSidebar/AIChat/ThinkingScratchpad.tsx +0 -156
  121. package/src/client/VZSidebar/AIChat/TypingIndicator.tsx +0 -19
  122. package/src/client/VZSidebar/AIChat/index.tsx +0 -273
  123. package/src/client/VZSidebar/AIChat/styles.scss +0 -627
  124. package/src/client/VZSidebar/AIChat/useSpeechRecognition.ts +0 -117
  125. package/src/client/VZSidebar/CreateDirModal.tsx +0 -111
  126. package/src/client/VZSidebar/CreateFileModal.tsx +0 -111
  127. package/src/client/VZSidebar/DeleteConfirmationModal.tsx +0 -76
  128. package/src/client/VZSidebar/DirectoryListing.tsx +0 -89
  129. package/src/client/VZSidebar/EmptyState.tsx +0 -11
  130. package/src/client/VZSidebar/FileListing.tsx +0 -91
  131. package/src/client/VZSidebar/FileTypeIcon.tsx +0 -49
  132. package/src/client/VZSidebar/Item.tsx +0 -237
  133. package/src/client/VZSidebar/Listing.tsx +0 -47
  134. package/src/client/VZSidebar/Search.tsx +0 -527
  135. package/src/client/VZSidebar/VisualEditor.tsx +0 -282
  136. package/src/client/VZSidebar/VoiceChatModal.tsx +0 -121
  137. package/src/client/VZSidebar/aiCopyPaste.ts +0 -199
  138. package/src/client/VZSidebar/index.tsx +0 -728
  139. package/src/client/VZSidebar/styles.scss +0 -612
  140. package/src/client/VZSidebar/useDragAndDrop.tsx +0 -408
  141. package/src/client/bootstrap.ts +0 -26
  142. package/src/client/buildWorker.ts +0 -3
  143. package/src/client/featureFlags.ts +0 -8
  144. package/src/client/generateRequestId.ts +0 -6
  145. package/src/client/getFileTree.ts +0 -53
  146. package/src/client/index.ts +0 -19
  147. package/src/client/main.jsx +0 -10
  148. package/src/client/presenceColor.ts +0 -8
  149. package/src/client/shouldTriggerRun.ts +0 -31
  150. package/src/client/sortFileTree.ts +0 -21
  151. package/src/client/tabsSearchParameters.test.ts +0 -79
  152. package/src/client/tabsSearchParameters.ts +0 -137
  153. package/src/client/themes/index.ts +0 -7
  154. package/src/client/themes/themes.ts +0 -77
  155. package/src/client/themes/uiwImports.ts +0 -32
  156. package/src/client/themes/useDynamicTheme.ts +0 -29
  157. package/src/client/themes/vizhubTheme/colors.ts +0 -80
  158. package/src/client/themes/vizhubTheme/index.ts +0 -193
  159. package/src/client/useActions.ts +0 -288
  160. package/src/client/useESLint/index.ts +0 -72
  161. package/src/client/useESLint/worker.ts +0 -113
  162. package/src/client/useEditorCache.ts +0 -35
  163. package/src/client/useFileCRUD.ts +0 -228
  164. package/src/client/useKeyboardShortcuts.ts +0 -451
  165. package/src/client/useOpenDirectories.ts +0 -99
  166. package/src/client/usePresenceAutoFollow.ts +0 -85
  167. package/src/client/usePrettier/index.ts +0 -178
  168. package/src/client/usePrettier/worker.ts +0 -109
  169. package/src/client/useRunCode.tsx +0 -15
  170. package/src/client/useRuntimeError.ts +0 -32
  171. package/src/client/useSubmitOperation.ts +0 -15
  172. package/src/client/useURLSync.ts +0 -137
  173. package/src/client/usernameLocalStorage.ts +0 -43
  174. package/src/client/utils/fileExtension.ts +0 -10
  175. package/src/client/utils/isImageFile.ts +0 -12
  176. package/src/client/vzReducer/aiChatReducer.ts +0 -44
  177. package/src/client/vzReducer/closeTabReducer.test.ts +0 -205
  178. package/src/client/vzReducer/closeTabsReducer.ts +0 -96
  179. package/src/client/vzReducer/createInitialState.ts +0 -38
  180. package/src/client/vzReducer/editorNoLongerWantsFocusReducer.ts +0 -9
  181. package/src/client/vzReducer/findPane.ts +0 -19
  182. package/src/client/vzReducer/index.ts +0 -250
  183. package/src/client/vzReducer/openTabReducer.test.ts +0 -127
  184. package/src/client/vzReducer/openTabReducer.ts +0 -74
  185. package/src/client/vzReducer/searchReducer.test.ts +0 -292
  186. package/src/client/vzReducer/searchReducer.ts +0 -181
  187. package/src/client/vzReducer/setActiveFileIdReducer.ts +0 -28
  188. package/src/client/vzReducer/setActiveFileLeftRightReducer.ts +0 -76
  189. package/src/client/vzReducer/setIsDocOpenReducer.ts +0 -9
  190. package/src/client/vzReducer/setIsSettingsOpenReducer.ts +0 -9
  191. package/src/client/vzReducer/setThemeReducer.ts +0 -9
  192. package/src/client/vzReducer/setUsernameReducer.ts +0 -9
  193. package/src/client/vzReducer/splitCurrentPaneReducer.ts +0 -66
  194. package/src/client/vzReducer/toggleAutoFollowReducer.ts +0 -12
  195. package/src/client/vzReducer/updatePane.ts +0 -40
  196. package/src/client/vzReducer/updatePresenceIndicatorReducer.ts +0 -30
  197. package/src/client/vzReducer/visualEditorReducer.ts +0 -9
  198. package/src/reactRouterExports.ts +0 -15
  199. package/src/runCode.ts +0 -27
  200. package/src/server/aiChatHandler/aiEditing.ts +0 -152
  201. package/src/server/aiChatHandler/chatOperations.ts +0 -510
  202. package/src/server/aiChatHandler/errorHandling.ts +0 -66
  203. package/src/server/aiChatHandler/index.ts +0 -200
  204. package/src/server/aiChatHandler/llmStreaming.ts +0 -419
  205. package/src/server/aiChatHandler/undoHandler.ts +0 -97
  206. package/src/server/aiChatHandler/validation.ts +0 -24
  207. package/src/server/computeInitialDocument.ts +0 -220
  208. package/src/server/generateAIResponse.ts +0 -188
  209. package/src/server/handleAIAssist.ts +0 -53
  210. package/src/server/handleAIChatUndo.ts +0 -2
  211. package/src/server/handleAICopilot.ts +0 -97
  212. package/src/server/index.ts +0 -388
  213. package/src/server/livekit.ts +0 -24
  214. package/src/server/prettier.ts +0 -115
  215. package/src/submitOperation.ts +0 -25
  216. package/src/types.ts +0 -205
  217. package/src/utils/fileDiff.ts +0 -117
  218. package/src/vite-env.d.ts +0 -10
  219. /package/{src/server/handleAIChatMessage.ts → dist/server/handleAIChatMessage.js} +0 -0
  220. /package/{src/server/isDirectory.ts → dist/server/isDirectory.js} +0 -0
@@ -1,228 +0,0 @@
1
- import { useCallback } from 'react';
2
- import { FileTreePath } from '../types';
3
- import { VizFileId, VizContent } from '@vizhub/viz-types';
4
- import { randomId } from '../randomId';
5
- import { EditorCache } from './useEditorCache';
6
- import { getFileExtension } from './utils/fileExtension';
7
- import { getLanguageExtension } from './CodeEditor/getOrCreateEditor';
8
- import { editorCacheKey } from './useEditorCache';
9
-
10
- // CRUD operations for files and directories
11
- // CRUD = Create, Read, Update, Delete
12
- export const useFileCRUD = ({
13
- submitOperation,
14
- closeTabs,
15
- openTab,
16
- editorCache,
17
- content,
18
- }: {
19
- submitOperation: (
20
- operation: (document: VizContent) => VizContent,
21
- ) => void;
22
- closeTabs: (idsToDelete: Array<VizFileId>) => void;
23
- openTab: ({
24
- fileId,
25
- isTransient,
26
- }: {
27
- fileId: VizFileId;
28
- isTransient: boolean;
29
- }) => void;
30
- editorCache: EditorCache;
31
- content: VizContent;
32
- }) => {
33
- // Create a new file
34
- const createFile = useCallback(
35
- (name: string, text = '') => {
36
- if (name) {
37
- const fileId: VizFileId = randomId();
38
- submitOperation((document: VizContent) => ({
39
- ...document,
40
- files: {
41
- ...document.files,
42
- [fileId]: { name, text },
43
- },
44
- }));
45
- // When a new file is created, open it in a new tab
46
- // and focus the editor on it.
47
- openTab({ fileId, isTransient: false });
48
- }
49
- },
50
- [submitOperation],
51
- );
52
-
53
- const createDirectory = useCallback(
54
- (name: string, text = null) => {
55
- if (name) {
56
- name += '/';
57
- const fileId: VizFileId = randomId();
58
- submitOperation((document: VizContent) => ({
59
- ...document,
60
- files: {
61
- ...document.files,
62
- [fileId]: { name, text },
63
- },
64
- }));
65
- }
66
- },
67
- [submitOperation],
68
- );
69
-
70
- // Called when a file in the sidebar is renamed.
71
- const renameFile = useCallback(
72
- (fileId: VizFileId, newName: string) => {
73
- submitOperation((document: VizContent) => ({
74
- ...document,
75
- files: {
76
- ...document.files,
77
- [fileId]: {
78
- ...document.files[fileId],
79
- name:
80
- document.files[fileId].name.substring(
81
- 0,
82
- document.files[fileId].name.lastIndexOf(
83
- '/',
84
- ) + 1,
85
- ) + newName,
86
- },
87
- },
88
- }));
89
-
90
- const oldName = content.files[fileId].name;
91
- const oldExtension = getFileExtension(oldName);
92
-
93
- const newExtension = getFileExtension(newName);
94
-
95
- const didExtensionChange =
96
- newExtension !== oldExtension;
97
- if (didExtensionChange) {
98
- // Update the language compartment of the corresponding CodeMirror editor
99
- // with the new language, based on the new extension, only if it changed.
100
- // Search through all cached editors for this file ID
101
- for (const [
102
- cacheKey,
103
- cachedEditor,
104
- ] of editorCache.entries()) {
105
- // Check if this cache entry is for the current file
106
- if (cacheKey.startsWith(fileId + '|')) {
107
- if (
108
- cachedEditor &&
109
- cachedEditor.languageCompartment
110
- ) {
111
- const newLanguageExtension =
112
- getLanguageExtension(newExtension);
113
-
114
- // Reconfigure the language compartment with the new language extension
115
- cachedEditor.editor.dispatch({
116
- effects:
117
- cachedEditor.languageCompartment.reconfigure(
118
- newLanguageExtension
119
- ? [newLanguageExtension]
120
- : [],
121
- ),
122
- });
123
- // Continue to update all instances of this file in different panes
124
- }
125
- }
126
- }
127
- }
128
- },
129
- [submitOperation],
130
- );
131
-
132
- // Renames a directory
133
- const renameDirectory = useCallback(
134
- (
135
- path: FileTreePath,
136
- oldName: string,
137
- newName: string,
138
- ) => {
139
- submitOperation((document: VizContent) => {
140
- const updatedFiles = Object.keys(
141
- document.files,
142
- ).reduce((acc, key) => {
143
- const file = document.files[key];
144
- const fileName = file.name;
145
- //See if it's actually in directory
146
- if (fileName.includes(path + '/')) {
147
- //Create New Name for System
148
- const pathPart = fileName.substring(
149
- 0,
150
- fileName.indexOf(path) + path.length,
151
- );
152
- const oldNamePos =
153
- pathPart.lastIndexOf(oldName);
154
- const fileNewName =
155
- fileName.substring(0, oldNamePos) +
156
- newName +
157
- fileName.substring(
158
- oldNamePos + oldName.length,
159
- );
160
- //Return with new names
161
- acc[key] = { ...file, name: fileNewName };
162
- } else {
163
- acc[key] = file;
164
- }
165
- return acc;
166
- }, {});
167
-
168
- return { ...document, files: updatedFiles };
169
- });
170
- },
171
- [submitOperation],
172
- );
173
-
174
- // Deletes a file
175
- const deleteFile = useCallback(
176
- (fileId: VizFileId) => {
177
- closeTabs([fileId]);
178
- submitOperation((document) => {
179
- const updatedFiles = { ...document.files };
180
- delete updatedFiles[fileId];
181
- return { ...document, files: updatedFiles };
182
- });
183
- },
184
- [submitOperation, closeTabs],
185
- );
186
-
187
- // Deletes a directory and all files within it
188
- const deleteDirectory = useCallback(
189
- (path: FileTreePath) => {
190
- const tabsToClose: Array<VizFileId> = [];
191
- submitOperation((document: VizContent) => {
192
- const updatedFiles = { ...document.files };
193
- for (const key in updatedFiles) {
194
- if (updatedFiles[key].name.includes(path + '/')) {
195
- tabsToClose.push(key);
196
- delete updatedFiles[key];
197
- }
198
- }
199
- return { ...document, files: updatedFiles };
200
- });
201
- closeTabs(tabsToClose);
202
- },
203
- [submitOperation, closeTabs],
204
- );
205
-
206
- // Deletes all files
207
- const deleteAllFiles = useCallback(() => {
208
- const tabsToClose: Array<VizFileId> = [];
209
- submitOperation((document: VizContent) => {
210
- // Collect all file IDs for closing tabs
211
- for (const fileId in document.files) {
212
- tabsToClose.push(fileId);
213
- }
214
- return { ...document, files: {} };
215
- });
216
- closeTabs(tabsToClose);
217
- }, [submitOperation, closeTabs]);
218
-
219
- return {
220
- createFile,
221
- renameFile,
222
- deleteFile,
223
- createDirectory,
224
- renameDirectory,
225
- deleteDirectory,
226
- deleteAllFiles,
227
- };
228
- };
@@ -1,451 +0,0 @@
1
- import { useContext, useEffect } from 'react';
2
- import { shouldTriggerRun } from './shouldTriggerRun';
3
- import { syntaxTree } from '@codemirror/language';
4
- import { SyntaxNode, SyntaxNodeRef } from '@lezer/common';
5
- import { EditorView } from '@codemirror/view';
6
- import { EditorState } from '@codemirror/state';
7
- import { VizFileId } from '@vizhub/viz-types';
8
- import { PaneId } from '../types';
9
- import { editorCacheKey } from './useEditorCache';
10
-
11
- /*
12
- The following is a helpful resource for the following Code Mirror Syntax Tree methods below
13
- https://lezer.codemirror.net/docs/ref/#common
14
- */
15
-
16
- // Store the element in the current editor DOM to highlight, indicating a potential jump to definition is possible
17
- let activeJumpingElement: HTMLSpanElement = null;
18
-
19
- // Store the syntax node representing the destination within the syntax tree
20
- let definingNode: SyntaxNode = null;
21
-
22
- // Example nesting types for the specific language to find level in the syntax tree
23
- const nestingTypes = new Set<string>([
24
- 'FunctionDeclaration',
25
- 'ClassDeclaration',
26
- 'MethodDeclaration',
27
- 'Block',
28
- 'IfStatement',
29
- 'ForStatement',
30
- 'WhileStatement',
31
- 'SwitchStatement',
32
- 'TryStatement',
33
- 'CatchClause',
34
- 'WithStatement',
35
- 'ArrowFunction',
36
- 'ImportGroup',
37
- ]);
38
-
39
- // Example declaration types for the specific language to find definitions in the syntax tree
40
- const declarationTypes = new Set<string>([
41
- 'VariableDeclaration',
42
- 'FunctionDeclaration',
43
- 'ClassDeclaration',
44
- 'MethodDeclaration',
45
- 'PropertyDeclaration',
46
- 'ImportDeclaration',
47
- 'ExportDeclaration',
48
- 'CallExpression',
49
- 'ArrayPattern',
50
- 'ObjectPattern',
51
- 'PatternProperty',
52
- 'ArgList',
53
- 'TypeArgList',
54
- 'ParamList',
55
- 'ForOfSpec',
56
- ]);
57
-
58
- function getIdentifierContext(
59
- identifier: SyntaxNode,
60
- ): number {
61
- let current: SyntaxNode = identifier;
62
- let levels: number = 0;
63
-
64
- // Traverse up the tree to find the total depth, only counting valid nesting types
65
- while (current && current.type) {
66
- const parentType: string = current.type.name;
67
-
68
- if (nestingTypes.has(parentType)) {
69
- levels++;
70
- }
71
-
72
- current = current.parent;
73
- }
74
-
75
- return levels;
76
- }
77
-
78
- function jumpToDefinition(
79
- editor: EditorView,
80
- node: SyntaxNode,
81
- ): SyntaxNode {
82
- const state: EditorState = editor.state;
83
- const definitions: Array<{
84
- identifier: SyntaxNode;
85
- context: number;
86
- }> = [];
87
-
88
- // From an identifier in the syntax tree, fetch the name and context to find closest defining syntax node
89
- const identifier: SyntaxNode = node;
90
- const identifierName: string = state.doc.sliceString(
91
- identifier.from,
92
- identifier.to,
93
- );
94
- const context: number = getIdentifierContext(identifier);
95
-
96
- if (identifier) {
97
- syntaxTree(state).iterate({
98
- enter(tree: SyntaxNodeRef) {
99
- // Traverse syntax tree to find positions of respective identifier definitions within context
100
- if (
101
- declarationTypes.has(tree.name) ||
102
- nestingTypes.has(tree.name)
103
- ) {
104
- const parent: SyntaxNode = tree.node;
105
-
106
- // Fetch a host of potential identifiers in an attempt to find the defining syntax node
107
- const children: Array<SyntaxNode> = [
108
- ...parent.getChildren('VariableDefinition'),
109
- ...parent.getChildren('PropertyDefinition'),
110
- ...parent.getChildren('PropertyName'),
111
- ...parent.getChildren('Identifier'),
112
- ];
113
-
114
- children.forEach((child: SyntaxNode) => {
115
- const name: string = state.doc.sliceString(
116
- child.from,
117
- child.to,
118
- );
119
-
120
- // Ensure no jump to self by comparing syntax node id's
121
- if (
122
- name === identifierName &&
123
- child.type.id !== identifier.type.id
124
- ) {
125
- definitions.push({
126
- identifier: child,
127
- context: getIdentifierContext(child),
128
- });
129
- }
130
- });
131
- }
132
- },
133
- });
134
-
135
- if (definitions.length > 0) {
136
- // Sort definitions by their context in the syntax tree
137
- definitions.sort((a, b) => a.context - b.context);
138
-
139
- let closestDefinition: SyntaxNode =
140
- definitions[0].identifier;
141
-
142
- for (let i = definitions.length - 1; i >= 0; i--) {
143
- if (definitions[i].context <= context) {
144
- closestDefinition = definitions[i].identifier;
145
- break;
146
- }
147
- }
148
-
149
- return closestDefinition;
150
- }
151
-
152
- return null;
153
- }
154
- }
155
-
156
- // Sidebar keyboard shortcuts in form Ctrl + Shift + <key>
157
- const sideBarKeyBoardMap = {
158
- E: 'files-icon',
159
- F: 'search-icon',
160
- K: 'shortcut-icon',
161
- B: 'bug-icon',
162
- S: 'settings-icon',
163
- N: 'new-file-icon',
164
- D: 'new-directory-icon',
165
- A: 'auto-focus-icon',
166
- };
167
-
168
- // This module implements the keyboard shortcuts
169
- // for the VZCode editor.
170
- // These include:
171
- // * Alt-w: Close the current tab
172
- // * Alt-n: Open the create file modal
173
- // * Alt-PageUp: Change the active tab to the previous one
174
- // * Alt-PageDown: Change the active tab to the next one
175
- // * Ctrl-s or Shift-Enter: Run the code and format it with Prettier
176
- // * Ctrl-Click: Jump to closest definition for a potential identifier
177
- export const useKeyboardShortcuts = ({
178
- closeTabs,
179
- activeFileId,
180
- activePaneId,
181
- handleOpenCreateFileModal,
182
- setActiveFileLeft,
183
- setActiveFileRight,
184
- toggleSearchFocused,
185
- runPrettierRef,
186
- runCodeRef,
187
- sidebarRef,
188
- editorCache,
189
- codeEditorRef,
190
- }: {
191
- closeTabs: (fileIds: VizFileId[]) => void;
192
- activeFileId: VizFileId | null;
193
- activePaneId: PaneId;
194
- handleOpenCreateFileModal: () => void;
195
- setActiveFileLeft: () => void;
196
- setActiveFileRight: () => void;
197
- toggleSearchFocused: () => void;
198
- runPrettierRef: React.MutableRefObject<() => void>;
199
- runCodeRef: React.MutableRefObject<() => void>;
200
- sidebarRef: React.RefObject<HTMLDivElement>;
201
- editorCache: Map<string, { editor: EditorView }>;
202
- codeEditorRef: React.RefObject<HTMLDivElement>;
203
- }) => {
204
- useEffect(() => {
205
- // This key is needed to look up the current editor in the editor cache.
206
- const cacheKey = editorCacheKey(
207
- activeFileId,
208
- activePaneId,
209
- );
210
-
211
- const handleKeyPress = (event: KeyboardEvent) => {
212
- if (shouldTriggerRun(event)) {
213
- event.preventDefault();
214
-
215
- // Run Prettier
216
- const runPrettier = runPrettierRef.current;
217
- if (runPrettier !== null) {
218
- runPrettier();
219
- }
220
-
221
- // Run the code
222
- const runCode = runCodeRef.current;
223
-
224
- if (runCode !== null) {
225
- runCode();
226
- }
227
- return;
228
- }
229
-
230
- if (event.ctrlKey && event.shiftKey) {
231
- // Handle keyboard shortcuts related to the side bar icons
232
- document
233
- .getElementById(sideBarKeyBoardMap[event.key])
234
- ?.click();
235
-
236
- // Ensure the search input is always focused
237
- if (event.key === 'F') {
238
- toggleSearchFocused();
239
- }
240
- } else if (event.ctrlKey && event.key === ',') {
241
- document
242
- .getElementById(sideBarKeyBoardMap['S'])
243
- ?.click();
244
- }
245
-
246
- if (event.ctrlKey === true) {
247
- // On holding CTRL key, search for a potential definition jump using mouse location
248
- document.addEventListener(
249
- 'mouseover',
250
- handleMouseOver,
251
- );
252
- }
253
-
254
- if (event.altKey === true) {
255
- // Alt-w: Close the current tab
256
- if (event.key === 'w') {
257
- // TODO clean this up so we can remove `activeFileId`
258
- // as a dependency
259
- // TODO closeActiveTab()
260
- if (activeFileId) {
261
- closeTabs([activeFileId]);
262
- }
263
- return;
264
- }
265
-
266
- // Alt-n: Open the create file modal
267
- if (event.key === 'n') {
268
- handleOpenCreateFileModal();
269
- return;
270
- }
271
-
272
- // Alt-PageUp: Change the active tab to the previous one
273
- if (event.key === 'PageUp') {
274
- setActiveFileLeft();
275
- return;
276
- }
277
-
278
- // Alt-PageDown: Change the active tab to the next one
279
- if (event.key === 'PageDown') {
280
- setActiveFileRight();
281
- return;
282
- }
283
-
284
- if (event.key === '1') {
285
- if (sidebarRef.current) {
286
- sidebarRef.current.focus();
287
- }
288
- }
289
-
290
- if (event.key === '2') {
291
- if (codeEditorRef.current) {
292
- codeEditorRef.current.focus();
293
- }
294
- }
295
- }
296
- };
297
-
298
- const resetActiveJumpingElement = (): void => {
299
- if (activeJumpingElement) {
300
- activeJumpingElement.style.cursor = 'initial';
301
- activeJumpingElement.style.textDecoration = 'none';
302
- activeJumpingElement = definingNode = null;
303
- }
304
-
305
- document.removeEventListener(
306
- 'mouseover',
307
- handleMouseOver,
308
- );
309
- document.removeEventListener(
310
- 'mousedown',
311
- jumpToDefinitionHandler,
312
- );
313
- };
314
-
315
- const jumpToDefinitionHandler = (
316
- event: MouseEvent,
317
- ): void => {
318
- // Ensure the current destination node is defined
319
- // and current cursor position matches highlighted element
320
- if (
321
- !definingNode ||
322
- (event.target as HTMLSpanElement) !==
323
- activeJumpingElement
324
- ) {
325
- return;
326
- }
327
-
328
- // Don't crash if no active file
329
- if (!activeFileId) {
330
- return;
331
- }
332
-
333
- // Move current cursor and center view in the editor to destination node
334
-
335
- const editor: EditorView =
336
- editorCache.get(cacheKey).editor;
337
- const closestDefinition: SyntaxNode = definingNode;
338
-
339
- editor.dispatch({
340
- selection: {
341
- anchor: closestDefinition.from,
342
- head: closestDefinition.to,
343
- },
344
- scrollIntoView: true,
345
- effects: EditorView.scrollIntoView(
346
- closestDefinition.from,
347
- {
348
- y: 'center',
349
- },
350
- ),
351
- });
352
-
353
- resetActiveJumpingElement();
354
- };
355
-
356
- const handleKeyRelease = (event: KeyboardEvent) => {
357
- // On releasing CTRL key, reset all active definition jumping elements and listeners
358
- if (!event.ctrlKey) {
359
- resetActiveJumpingElement();
360
- }
361
- };
362
-
363
- const handleMouseOver = (event: MouseEvent) => {
364
- if (!activeFileId) {
365
- return;
366
- }
367
-
368
- const editor: EditorView =
369
- editorCache.get(cacheKey).editor;
370
- const tree = syntaxTree(editor.state);
371
- const element = event.target as HTMLSpanElement;
372
-
373
- // Ensure the identifier element can be found and is within the current editor DOM
374
- if (
375
- element == null ||
376
- !editor.dom.contains(element)
377
- ) {
378
- return;
379
- }
380
-
381
- let position: number;
382
- try {
383
- position = editor.posAtDOM(element);
384
- } catch (error) {
385
- // Element is not within the current editor's document
386
- return;
387
- }
388
- const identifier: SyntaxNode = tree.resolveInner(
389
- position,
390
- 1,
391
- );
392
-
393
- // All valid identifiers must be span elements to find a potential defining jump in editor
394
- if (
395
- identifier &&
396
- element instanceof HTMLSpanElement
397
- ) {
398
- const potentialJump: SyntaxNode = jumpToDefinition(
399
- editor,
400
- identifier,
401
- );
402
-
403
- // Only allowing to jump to other definition nodes
404
- if (
405
- potentialJump &&
406
- identifier.type.id !== potentialJump.type.id
407
- ) {
408
- if (activeJumpingElement) {
409
- activeJumpingElement.style.cursor = 'initial';
410
- activeJumpingElement.style.textDecoration =
411
- 'none';
412
- }
413
-
414
- activeJumpingElement = element;
415
- definingNode = potentialJump;
416
-
417
- activeJumpingElement.style.cursor = 'pointer';
418
- activeJumpingElement.style.textDecoration =
419
- 'underline';
420
-
421
- // CTRL + Click: Jump to relative definition, which is removed on CTRL key release
422
- document.addEventListener(
423
- 'mousedown',
424
- jumpToDefinitionHandler,
425
- { once: true },
426
- );
427
- }
428
- }
429
- };
430
-
431
- document.addEventListener('keydown', handleKeyPress);
432
- document.addEventListener('keyup', handleKeyRelease);
433
-
434
- return () => {
435
- document.removeEventListener(
436
- 'keydown',
437
- handleKeyPress,
438
- );
439
- document.removeEventListener(
440
- 'keyup',
441
- handleKeyRelease,
442
- );
443
- };
444
- }, [
445
- handleOpenCreateFileModal,
446
- closeTabs,
447
- activeFileId,
448
- setActiveFileLeft,
449
- setActiveFileRight,
450
- ]);
451
- };