2024-04-25 00:13:09 -07:00
|
|
|
import { appConfigDir } from '@tauri-apps/api/path'
|
2023-08-15 21:56:24 -04:00
|
|
|
import { isTauri } from './isTauri'
|
2024-04-25 00:13:09 -07:00
|
|
|
import type { FileEntry } from 'lib/types'
|
2024-04-02 10:29:34 -04:00
|
|
|
import {
|
|
|
|
INDEX_IDENTIFIER,
|
|
|
|
MAX_PADDING,
|
2024-04-19 10:50:58 -04:00
|
|
|
ONBOARDING_PROJECT_NAME,
|
2024-04-02 10:29:34 -04:00
|
|
|
PROJECT_ENTRYPOINT,
|
|
|
|
} from 'lib/constants'
|
2024-04-19 10:50:58 -04:00
|
|
|
import { bracket } from './exampleKcl'
|
|
|
|
import { paths } from './paths'
|
2024-04-25 00:13:09 -07:00
|
|
|
import {
|
|
|
|
createNewProjectDirectory,
|
|
|
|
listProjects,
|
|
|
|
readAppSettingsFile,
|
|
|
|
} from './tauri'
|
2023-08-28 20:31:49 -04:00
|
|
|
|
2023-10-16 13:28:41 -04:00
|
|
|
export const isHidden = (fileOrDir: FileEntry) =>
|
|
|
|
!!fileOrDir.name?.startsWith('.')
|
|
|
|
|
|
|
|
export const isDir = (fileOrDir: FileEntry) =>
|
|
|
|
'children' in fileOrDir && fileOrDir.children !== undefined
|
|
|
|
|
|
|
|
// Deeply sort the files and directories in a project like VS Code does:
|
|
|
|
// The main.kcl file is always first, then files, then directories
|
|
|
|
// Files and directories are sorted alphabetically
|
|
|
|
export function sortProject(project: FileEntry[]): FileEntry[] {
|
|
|
|
const sortedProject = project.sort((a, b) => {
|
|
|
|
if (a.name === PROJECT_ENTRYPOINT) {
|
|
|
|
return -1
|
|
|
|
} else if (b.name === PROJECT_ENTRYPOINT) {
|
|
|
|
return 1
|
|
|
|
} else if (a.children === undefined && b.children !== undefined) {
|
|
|
|
return -1
|
|
|
|
} else if (a.children !== undefined && b.children === undefined) {
|
|
|
|
return 1
|
|
|
|
} else if (a.name && b.name) {
|
|
|
|
return a.name.localeCompare(b.name)
|
|
|
|
} else {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
return sortedProject.map((fileOrDir: FileEntry) => {
|
|
|
|
if ('children' in fileOrDir && fileOrDir.children !== undefined) {
|
|
|
|
return {
|
|
|
|
...fileOrDir,
|
|
|
|
children: sortProject(fileOrDir.children),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return fileOrDir
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-08-15 21:56:24 -04:00
|
|
|
// create a regex to match the project name
|
|
|
|
// replacing any instances of "$n" with a regex to match any number
|
|
|
|
function interpolateProjectName(projectName: string) {
|
|
|
|
const regex = new RegExp(
|
|
|
|
projectName.replace(getPaddedIdentifierRegExp(), '([0-9]+)')
|
|
|
|
)
|
|
|
|
return regex
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns the next available index for a project name
|
|
|
|
export function getNextProjectIndex(projectName: string, files: FileEntry[]) {
|
|
|
|
const regex = interpolateProjectName(projectName)
|
|
|
|
const matches = files.map((file) => file.name?.match(regex))
|
|
|
|
const indices = matches
|
|
|
|
.filter(Boolean)
|
|
|
|
.map((match) => match![1])
|
|
|
|
.map(Number)
|
|
|
|
const maxIndex = Math.max(...indices, -1)
|
|
|
|
return maxIndex + 1
|
|
|
|
}
|
|
|
|
|
|
|
|
// Interpolates the project name with the next available index,
|
|
|
|
// padding the index with 0s if necessary
|
|
|
|
export function interpolateProjectNameWithIndex(
|
|
|
|
projectName: string,
|
|
|
|
index: number
|
|
|
|
) {
|
|
|
|
const regex = getPaddedIdentifierRegExp()
|
|
|
|
|
|
|
|
const matches = projectName.match(regex)
|
|
|
|
const padStartLength = Math.min(
|
|
|
|
matches !== null ? matches[1]?.length || 0 : 0,
|
|
|
|
MAX_PADDING
|
|
|
|
)
|
|
|
|
return projectName.replace(
|
|
|
|
regex,
|
|
|
|
index.toString().padStart(padStartLength + 1, '0')
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export function doesProjectNameNeedInterpolated(projectName: string) {
|
|
|
|
return projectName.includes(INDEX_IDENTIFIER)
|
|
|
|
}
|
|
|
|
|
|
|
|
function escapeRegExpChars(string: string) {
|
|
|
|
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
|
|
}
|
|
|
|
|
|
|
|
function getPaddedIdentifierRegExp() {
|
|
|
|
const escapedIdentifier = escapeRegExpChars(INDEX_IDENTIFIER)
|
|
|
|
return new RegExp(`${escapedIdentifier}(${escapedIdentifier.slice(-1)}*)`)
|
|
|
|
}
|
2024-03-14 15:56:45 -04:00
|
|
|
|
2024-04-02 10:29:34 -04:00
|
|
|
export async function getSettingsFolderPaths(projectPath?: string) {
|
|
|
|
const user = isTauri() ? await appConfigDir() : '/'
|
|
|
|
const project = projectPath !== undefined ? projectPath : undefined
|
|
|
|
|
|
|
|
return {
|
|
|
|
user,
|
|
|
|
project,
|
2024-03-14 15:56:45 -04:00
|
|
|
}
|
|
|
|
}
|
2024-04-19 10:50:58 -04:00
|
|
|
|
|
|
|
export async function createAndOpenNewProject(
|
|
|
|
navigate: (path: string) => void
|
|
|
|
) {
|
2024-04-25 00:13:09 -07:00
|
|
|
const configuration = await readAppSettingsFile()
|
|
|
|
const projects = await listProjects(configuration)
|
|
|
|
const nextIndex = getNextProjectIndex(ONBOARDING_PROJECT_NAME, projects)
|
2024-04-19 10:50:58 -04:00
|
|
|
const name = interpolateProjectNameWithIndex(
|
|
|
|
ONBOARDING_PROJECT_NAME,
|
|
|
|
nextIndex
|
|
|
|
)
|
2024-04-25 00:13:09 -07:00
|
|
|
const newFile = await createNewProjectDirectory(name, bracket, configuration)
|
2024-04-19 10:50:58 -04:00
|
|
|
navigate(`${paths.FILE}/${encodeURIComponent(newFile.path)}`)
|
|
|
|
}
|