use-file-tree-command-source.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import { useCallback, useMemo } from 'react'
  2. import { CommandPaletteSearchResult, CommandPaletteSource } from '../types'
  3. import { useFileTreeData } from '@/shared/context/file-tree-data-context'
  4. import { Folder } from '@ol-types/folder'
  5. import MiniSearch from 'minisearch'
  6. import { findInTree } from '@/features/file-tree/util/find-in-tree'
  7. import { debugConsole } from '@/utils/debugging'
  8. import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context'
  9. type FlatFileTree = { path: string; name: string; id: string }[]
  10. const useFileTreeCommandSource = (): CommandPaletteSource => {
  11. const { fileTreeData } = useFileTreeData()
  12. const { openDocWithId, openFileWithId } = useEditorManagerContext()
  13. const flatFileTree = useMemo(
  14. () => flattenFileTree(fileTreeData),
  15. [fileTreeData]
  16. )
  17. const index = useMemo(
  18. () => flatFileTree && indexFromFlatFileTree(flatFileTree),
  19. [flatFileTree]
  20. )
  21. const onSelect = useCallback(
  22. async (id: string) => {
  23. if (!fileTreeData) {
  24. return
  25. }
  26. const file = findInTree(fileTreeData, id)
  27. if (!file) {
  28. return
  29. }
  30. if (file.type === 'doc') {
  31. await openDocWithId(file.entity._id)
  32. } else if (file.type === 'fileRef') {
  33. openFileWithId(file.entity._id)
  34. } else {
  35. debugConsole.error('Attempting to open invalid entity type')
  36. }
  37. },
  38. [fileTreeData, openDocWithId, openFileWithId]
  39. )
  40. const defaults = useCallback((): CommandPaletteSearchResult[] => {
  41. if (!flatFileTree) {
  42. return []
  43. }
  44. const files: CommandPaletteSearchResult[] = flatFileTree
  45. .slice(0, 10)
  46. .map(({ path, name, id }) => ({
  47. title: name,
  48. description: path === name ? undefined : path,
  49. onSelect: () => onSelect(id),
  50. score: 1,
  51. }))
  52. return files
  53. }, [flatFileTree, onSelect])
  54. const source: CommandPaletteSource = useMemo(
  55. () => ({
  56. id: 'file-tree',
  57. search(query) {
  58. if (!index) {
  59. return []
  60. }
  61. const result = index.search(query, {
  62. prefix: true,
  63. fuzzy: term => (term.length > 3 ? 0.2 : false),
  64. })
  65. return result.map(({ path, name, id, score }) => ({
  66. title: name,
  67. description: path === name ? undefined : path,
  68. onSelect: () => onSelect(id),
  69. score,
  70. }))
  71. },
  72. defaults,
  73. }),
  74. [index, onSelect, defaults]
  75. )
  76. return source
  77. }
  78. export default useFileTreeCommandSource
  79. const flattenFileTree = (
  80. folder: Folder,
  81. parentPath = ''
  82. ): FlatFileTree | null => {
  83. if (!folder) {
  84. return null
  85. }
  86. const currentPath = `${parentPath}/${folder.name}`
  87. const files = folder.fileRefs.map(file => ({
  88. path: `${currentPath}/${file.name}`.replace(/^\/rootFolder\//, ''),
  89. name: file.name,
  90. id: file._id,
  91. }))
  92. const docs = folder.docs.map(doc => ({
  93. path: `${currentPath}/${doc.name}`.replace(/^\/rootFolder\//, ''),
  94. name: doc.name,
  95. id: doc._id,
  96. }))
  97. const subFolders = folder.folders
  98. .flatMap(subFolder => flattenFileTree(subFolder, currentPath))
  99. .filter(x => x !== null)
  100. return [...docs, ...files, ...subFolders]
  101. }
  102. function indexFromFlatFileTree(flatFileTree: FlatFileTree): MiniSearch {
  103. const miniSearch = new MiniSearch({
  104. fields: ['path', 'name'],
  105. storeFields: ['path', 'name'],
  106. })
  107. if (!flatFileTree) {
  108. return miniSearch
  109. }
  110. miniSearch.addAll(flatFileTree)
  111. return miniSearch
  112. }