fix project list showing projects of double clicked files (#2441)
* make sure there is at least one kcl file in the dir to show in list Signed-off-by: Jess Frazelle <github@jessfraz.com> * open the correct file not assuming main.kcl Signed-off-by: Jess Frazelle <github@jessfraz.com> * add file path tests Signed-off-by: Jess Frazelle <github@jessfraz.com> * update settings paths Signed-off-by: Jess Frazelle <github@jessfraz.com> * new images Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com>
49
src-tauri/Cargo.lock
generated
@ -212,6 +212,15 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
|
||||
dependencies = [
|
||||
"derive_arbitrary",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.4"
|
||||
@ -1207,6 +1216,17 @@ dependencies = [
|
||||
"syn 2.0.65",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_arbitrary"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.65",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.17"
|
||||
@ -1258,6 +1278,17 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.65",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dlib"
|
||||
version = "0.5.2"
|
||||
@ -2579,7 +2610,7 @@ dependencies = [
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"winnow 0.5.40",
|
||||
"zip",
|
||||
"zip 1.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5471,7 +5502,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"url",
|
||||
"windows-sys 0.52.0",
|
||||
"zip",
|
||||
"zip 0.6.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -7123,6 +7154,20 @@ dependencies = [
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1f4a27345eb6f7aa7bd015ba7eb4175fa4e1b462a29874b779e0bbcf96c6ac7"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"crc32fast",
|
||||
"crossbeam-utils",
|
||||
"displaydoc",
|
||||
"indexmap 2.2.6",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.11.2+zstd.1.5.2"
|
||||
|
@ -121,11 +121,8 @@ async fn write_app_settings_file(app: tauri::AppHandle, configuration: Configura
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_project_settings_file_path(
|
||||
app_settings: Configuration,
|
||||
project_name: &str,
|
||||
) -> Result<PathBuf, InvokeError> {
|
||||
let project_dir = app_settings.settings.project.directory.join(project_name);
|
||||
async fn get_project_settings_file_path(project_path: &str) -> Result<PathBuf, InvokeError> {
|
||||
let project_dir = std::path::Path::new(project_path);
|
||||
|
||||
if !project_dir.exists() {
|
||||
tokio::fs::create_dir_all(&project_dir)
|
||||
@ -137,11 +134,8 @@ async fn get_project_settings_file_path(
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn read_project_settings_file(
|
||||
app_settings: Configuration,
|
||||
project_name: &str,
|
||||
) -> Result<ProjectConfiguration, InvokeError> {
|
||||
let settings_path = get_project_settings_file_path(app_settings, project_name).await?;
|
||||
async fn read_project_settings_file(project_path: &str) -> Result<ProjectConfiguration, InvokeError> {
|
||||
let settings_path = get_project_settings_file_path(project_path).await?;
|
||||
|
||||
// Check if this file exists.
|
||||
if !settings_path.exists() {
|
||||
@ -159,11 +153,10 @@ async fn read_project_settings_file(
|
||||
|
||||
#[tauri::command]
|
||||
async fn write_project_settings_file(
|
||||
app_settings: Configuration,
|
||||
project_name: &str,
|
||||
project_path: &str,
|
||||
configuration: ProjectConfiguration,
|
||||
) -> Result<(), InvokeError> {
|
||||
let settings_path = get_project_settings_file_path(app_settings, project_name).await?;
|
||||
let settings_path = get_project_settings_file_path(project_path).await?;
|
||||
let contents = toml::to_string_pretty(&configuration).map_err(|e| InvokeError::from_anyhow(e.into()))?;
|
||||
tokio::fs::write(settings_path, contents.as_bytes())
|
||||
.await
|
||||
|
@ -22,8 +22,7 @@ import {
|
||||
LspWorker,
|
||||
} from 'editor/plugins/lsp/types'
|
||||
import { wasmUrl } from 'lang/wasm'
|
||||
|
||||
const DEFAULT_FILE_NAME: string = 'main.kcl'
|
||||
import { PROJECT_ENTRYPOINT } from 'lib/constants'
|
||||
|
||||
function getWorkspaceFolders(): LSP.WorkspaceFolder[] {
|
||||
return []
|
||||
@ -137,7 +136,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
if (isKclLspServerReady && !TEST && kclLspClient) {
|
||||
// Set up the lsp plugin.
|
||||
const lsp = kclLanguage({
|
||||
documentUri: `file:///${DEFAULT_FILE_NAME}`,
|
||||
documentUri: `file:///${PROJECT_ENTRYPOINT}`,
|
||||
workspaceFolders: getWorkspaceFolders(),
|
||||
client: kclLspClient,
|
||||
})
|
||||
@ -211,7 +210,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
if (isCopilotLspServerReady && !TEST && copilotLspClient) {
|
||||
// Set up the lsp plugin.
|
||||
const lsp = copilotPlugin({
|
||||
documentUri: `file:///${DEFAULT_FILE_NAME}`,
|
||||
documentUri: `file:///${PROJECT_ENTRYPOINT}`,
|
||||
workspaceFolders: getWorkspaceFolders(),
|
||||
client: copilotLspClient,
|
||||
allowHTMLContent: true,
|
||||
@ -236,7 +235,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
redirect: boolean
|
||||
) => {
|
||||
const currentFilePath = projectBasename(
|
||||
file?.path || DEFAULT_FILE_NAME,
|
||||
file?.path || PROJECT_ENTRYPOINT,
|
||||
projectPath || ''
|
||||
)
|
||||
lspClients.forEach((lspClient) => {
|
||||
@ -267,7 +266,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
if (file) {
|
||||
// Send that the file was opened.
|
||||
const filename = projectBasename(
|
||||
file?.path || DEFAULT_FILE_NAME,
|
||||
file?.path || PROJECT_ENTRYPOINT,
|
||||
project?.path || ''
|
||||
)
|
||||
lspClients.forEach((lspClient) => {
|
||||
@ -285,7 +284,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
|
||||
const onFileOpen = (filePath: string | null, projectPath: string | null) => {
|
||||
const currentFilePath = projectBasename(
|
||||
filePath || DEFAULT_FILE_NAME,
|
||||
filePath || PROJECT_ENTRYPOINT,
|
||||
projectPath || ''
|
||||
)
|
||||
lspClients.forEach((lspClient) => {
|
||||
@ -302,7 +301,7 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
|
||||
const onFileClose = (filePath: string | null, projectPath: string | null) => {
|
||||
const currentFilePath = projectBasename(
|
||||
filePath || DEFAULT_FILE_NAME,
|
||||
filePath || PROJECT_ENTRYPOINT,
|
||||
projectPath || ''
|
||||
)
|
||||
lspClients.forEach((lspClient) => {
|
||||
|
@ -119,7 +119,7 @@ function ProjectCard({
|
||||
<>
|
||||
<Link
|
||||
className="relative z-0 flex flex-col h-full gap-2 p-1 !no-underline !text-chalkboard-110 dark:!text-chalkboard-10"
|
||||
to={`${paths.FILE}/${encodeURIComponent(project.path)}`}
|
||||
to={`${paths.FILE}/${encodeURIComponent(project.default_file)}`}
|
||||
data-testid="project-link"
|
||||
>
|
||||
<div className="flex-1">{project.name?.replace(FILE_EXT, '')}</div>
|
||||
|
@ -24,6 +24,7 @@ const projectWellFormed = {
|
||||
},
|
||||
kcl_file_count: 1,
|
||||
directory_count: 0,
|
||||
default_file: '/some/path/Simple Box/main.kcl',
|
||||
} satisfies Project
|
||||
|
||||
describe('ProjectSidebarMenu tests', () => {
|
||||
|
@ -172,7 +172,7 @@ export const SettingsAuthProviderBase = ({
|
||||
},
|
||||
'Execute AST': () => kclManager.executeCode(true),
|
||||
persistSettings: (context) =>
|
||||
saveSettings(context, loadedProject?.project?.name),
|
||||
saveSettings(context, loadedProject?.project?.path),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
@ -38,8 +38,8 @@ export const settingsLoader: LoaderFunction = async ({
|
||||
configuration
|
||||
)
|
||||
if (projectPathData) {
|
||||
const { project_name } = projectPathData
|
||||
const { settings: s } = await loadAndValidateSettings(project_name)
|
||||
const { project_path } = projectPathData
|
||||
const { settings: s } = await loadAndValidateSettings(project_path)
|
||||
settings = s
|
||||
}
|
||||
}
|
||||
@ -118,6 +118,7 @@ export const fileLoader: LoaderFunction = async ({
|
||||
children: [],
|
||||
kcl_file_count: 0,
|
||||
directory_count: 0,
|
||||
default_file: project_path,
|
||||
},
|
||||
file: {
|
||||
name: current_file_name,
|
||||
|
@ -147,7 +147,7 @@ export interface AppSettings {
|
||||
}
|
||||
|
||||
export async function loadAndValidateSettings(
|
||||
projectName?: string
|
||||
projectPath?: string
|
||||
): Promise<AppSettings> {
|
||||
const settings = createSettings()
|
||||
const inTauri = isTauri()
|
||||
@ -166,9 +166,9 @@ export async function loadAndValidateSettings(
|
||||
setSettingsAtLevel(settings, 'user', appSettingsPayload)
|
||||
|
||||
// Load the project settings if they exist
|
||||
if (projectName) {
|
||||
if (projectPath) {
|
||||
const projectSettings = inTauri
|
||||
? await readProjectSettingsFile(appSettings, projectName)
|
||||
? await readProjectSettingsFile(projectPath)
|
||||
: readLocalStorageProjectSettingsFile()
|
||||
|
||||
const projectSettingsPayload =
|
||||
@ -182,7 +182,7 @@ export async function loadAndValidateSettings(
|
||||
|
||||
export async function saveSettings(
|
||||
allSettings: typeof settings,
|
||||
projectName?: string
|
||||
projectPath?: string
|
||||
) {
|
||||
// Make sure we have wasm initialized.
|
||||
await initPromise
|
||||
@ -204,7 +204,7 @@ export async function saveSettings(
|
||||
)
|
||||
}
|
||||
|
||||
if (!projectName) {
|
||||
if (!projectPath) {
|
||||
// If we're not saving project settings, we're done.
|
||||
return
|
||||
}
|
||||
@ -217,7 +217,7 @@ export async function saveSettings(
|
||||
|
||||
// Write the project settings.
|
||||
if (inTauri) {
|
||||
await writeProjectSettingsFile(appSettings, projectName, projectSettings)
|
||||
await writeProjectSettingsFile(projectPath, projectSettings)
|
||||
} else {
|
||||
localStorage.setItem(
|
||||
localStorageProjectSettingsPath(),
|
||||
|
@ -143,24 +143,20 @@ export async function writeAppSettingsFile(
|
||||
|
||||
// Read project settings file.
|
||||
export async function readProjectSettingsFile(
|
||||
appSettings: Configuration,
|
||||
projectName: string
|
||||
projectPath: string
|
||||
): Promise<ProjectConfiguration> {
|
||||
return await invoke<ProjectConfiguration>('read_project_settings_file', {
|
||||
appSettings,
|
||||
projectName,
|
||||
projectPath,
|
||||
})
|
||||
}
|
||||
|
||||
// Write project settings file.
|
||||
export async function writeProjectSettingsFile(
|
||||
appSettings: Configuration,
|
||||
projectName: string,
|
||||
projectPath: string,
|
||||
settings: ProjectConfiguration
|
||||
): Promise<void> {
|
||||
return await invoke('write_project_settings_file', {
|
||||
appSettings,
|
||||
projectName,
|
||||
projectPath,
|
||||
configuration: settings,
|
||||
})
|
||||
}
|
||||
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 80 KiB |
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
@ -1,5 +1,6 @@
|
||||
//! Types for interacting with files in projects.
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
@ -7,7 +8,7 @@ use parse_display::{Display, FromStr};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::settings::types::{Configuration, DEFAULT_PROJECT_KCL_FILE};
|
||||
use crate::settings::types::Configuration;
|
||||
|
||||
/// State management for the application.
|
||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, PartialEq)]
|
||||
@ -48,7 +49,7 @@ impl ProjectState {
|
||||
.map_err(|e| anyhow::anyhow!("Error loading project from path {}: {:?}", source_path.display(), e))?;
|
||||
|
||||
// Check if we have a main.kcl file in the project.
|
||||
let project_file = source_path.join(DEFAULT_PROJECT_KCL_FILE);
|
||||
let project_file = source_path.join(crate::settings::types::DEFAULT_PROJECT_KCL_FILE);
|
||||
|
||||
if !project_file.exists() {
|
||||
// Create the default file in the project.
|
||||
@ -247,6 +248,8 @@ pub struct Project {
|
||||
#[serde(default)]
|
||||
#[ts(type = "number")]
|
||||
pub directory_count: u64,
|
||||
/// The default file to open on load.
|
||||
pub default_file: String,
|
||||
}
|
||||
|
||||
impl Project {
|
||||
@ -266,12 +269,13 @@ impl Project {
|
||||
}
|
||||
|
||||
let file = crate::settings::utils::walk_dir(&path).await?;
|
||||
let metadata = std::fs::metadata(path).ok().map(|m| m.into());
|
||||
let metadata = std::fs::metadata(&path).ok().map(|m| m.into());
|
||||
let mut project = Self {
|
||||
file,
|
||||
file: file.clone(),
|
||||
metadata,
|
||||
kcl_file_count: 0,
|
||||
directory_count: 0,
|
||||
default_file: get_default_kcl_file_for_dir(path, file).await?,
|
||||
};
|
||||
project.populate_kcl_file_count()?;
|
||||
project.populate_directory_count()?;
|
||||
@ -309,6 +313,40 @@ impl Project {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the default KCL file for a directory.
|
||||
/// This determines what the default file to open is.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[async_recursion::async_recursion]
|
||||
pub async fn get_default_kcl_file_for_dir<P>(dir: P, file: FileEntry) -> Result<String>
|
||||
where
|
||||
P: AsRef<Path> + Send,
|
||||
{
|
||||
// Make sure the dir is a directory.
|
||||
if !dir.as_ref().is_dir() {
|
||||
return Err(anyhow::anyhow!("Path `{}` is not a directory", dir.as_ref().display()));
|
||||
}
|
||||
|
||||
let default_file = dir.as_ref().join(crate::settings::types::DEFAULT_PROJECT_KCL_FILE);
|
||||
if !default_file.exists() {
|
||||
// Find a kcl file in the directory.
|
||||
if let Some(children) = file.children {
|
||||
for entry in children.iter() {
|
||||
if entry.name.ends_with(".kcl") {
|
||||
return Ok(dir.as_ref().join(&entry.name).display().to_string());
|
||||
} else if entry.children.is_some() {
|
||||
// Recursively find a kcl file in the directory.
|
||||
return get_default_kcl_file_for_dir(entry.path.clone(), entry.clone()).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't find a kcl file, create one.
|
||||
tokio::fs::write(&default_file, vec![]).await?;
|
||||
}
|
||||
|
||||
Ok(default_file.display().to_string())
|
||||
}
|
||||
|
||||
/// Information about a file or directory.
|
||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, PartialEq)]
|
||||
#[ts(export)]
|
||||
@ -588,4 +626,100 @@ mod tests {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_project_route_from_route_non_main_file() {
|
||||
let mut configuration = crate::settings::types::Configuration::default();
|
||||
configuration.settings.project.directory =
|
||||
std::path::PathBuf::from("/Users/macinatormax/Documents/kittycad-modeling-projects");
|
||||
|
||||
let route = "/Users/macinatormax/Documents/kittycad-modeling-projects/assembly/thing.kcl";
|
||||
let state = super::ProjectRoute::from_route(&configuration, route).unwrap();
|
||||
assert_eq!(
|
||||
state,
|
||||
super::ProjectRoute {
|
||||
project_name: Some("assembly".to_string()),
|
||||
project_path: "/Users/macinatormax/Documents/kittycad-modeling-projects/assembly".to_string(),
|
||||
current_file_name: Some("thing.kcl".to_string()),
|
||||
current_file_path: Some(
|
||||
"/Users/macinatormax/Documents/kittycad-modeling-projects/assembly/thing.kcl".to_string()
|
||||
),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_default_kcl_file_for_dir_non_exist() {
|
||||
let name = format!("kittycad-modeling-projects-{}", uuid::Uuid::new_v4());
|
||||
let dir = std::env::temp_dir().join(&name);
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let file = crate::settings::utils::walk_dir(&dir).await.unwrap();
|
||||
|
||||
let default_file = super::get_default_kcl_file_for_dir(&dir, file).await.unwrap();
|
||||
assert_eq!(default_file, dir.join("main.kcl").display().to_string());
|
||||
|
||||
std::fs::remove_dir_all(dir).unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_default_kcl_file_for_dir_main_kcl() {
|
||||
let name = format!("kittycad-modeling-projects-{}", uuid::Uuid::new_v4());
|
||||
let dir = std::env::temp_dir().join(&name);
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
std::fs::write(dir.join("main.kcl"), vec![]).unwrap();
|
||||
let file = crate::settings::utils::walk_dir(&dir).await.unwrap();
|
||||
|
||||
let default_file = super::get_default_kcl_file_for_dir(&dir, file).await.unwrap();
|
||||
assert_eq!(default_file, dir.join("main.kcl").display().to_string());
|
||||
|
||||
std::fs::remove_dir_all(dir).unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_default_kcl_file_for_dir_thing_kcl() {
|
||||
let name = format!("kittycad-modeling-projects-{}", uuid::Uuid::new_v4());
|
||||
let dir = std::env::temp_dir().join(&name);
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
std::fs::write(dir.join("thing.kcl"), vec![]).unwrap();
|
||||
let file = crate::settings::utils::walk_dir(&dir).await.unwrap();
|
||||
|
||||
let default_file = super::get_default_kcl_file_for_dir(&dir, file).await.unwrap();
|
||||
assert_eq!(default_file, dir.join("thing.kcl").display().to_string());
|
||||
std::fs::remove_dir_all(dir).unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_default_kcl_file_for_dir_nested_main_kcl() {
|
||||
let name = format!("kittycad-modeling-projects-{}", uuid::Uuid::new_v4());
|
||||
let dir = std::env::temp_dir().join(&name);
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
std::fs::create_dir_all(dir.join("assembly")).unwrap();
|
||||
std::fs::write(dir.join("assembly").join("main.kcl"), vec![]).unwrap();
|
||||
let file = crate::settings::utils::walk_dir(&dir).await.unwrap();
|
||||
|
||||
let default_file = super::get_default_kcl_file_for_dir(&dir, file).await.unwrap();
|
||||
assert_eq!(
|
||||
default_file,
|
||||
dir.join("assembly").join("main.kcl").display().to_string()
|
||||
);
|
||||
|
||||
std::fs::remove_dir_all(dir).unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_default_kcl_file_for_dir_nested_thing_kcl() {
|
||||
let name = format!("kittycad-modeling-projects-{}", uuid::Uuid::new_v4());
|
||||
let dir = std::env::temp_dir().join(&name);
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
std::fs::create_dir_all(dir.join("assembly")).unwrap();
|
||||
std::fs::write(dir.join("assembly").join("thing.kcl"), vec![]).unwrap();
|
||||
let file = crate::settings::utils::walk_dir(&dir).await.unwrap();
|
||||
|
||||
let default_file = super::get_default_kcl_file_for_dir(&dir, file).await.unwrap();
|
||||
assert_eq!(
|
||||
default_file,
|
||||
dir.join("assembly").join("thing.kcl").display().to_string()
|
||||
);
|
||||
std::fs::remove_dir_all(dir).unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -109,6 +109,7 @@ impl Configuration {
|
||||
// Because we just created it and it's empty.
|
||||
children: None,
|
||||
},
|
||||
default_file: project_file.to_string_lossy().to_string(),
|
||||
metadata: Some(tokio::fs::metadata(&project_dir).await?.into()),
|
||||
kcl_file_count: 1,
|
||||
directory_count: 0,
|
||||
@ -130,7 +131,13 @@ impl Configuration {
|
||||
continue;
|
||||
}
|
||||
|
||||
projects.push(self.get_project_info(&e.path().display().to_string()).await?);
|
||||
// Make sure the project has at least one kcl file in it.
|
||||
let project = self.get_project_info(&e.path().display().to_string()).await?;
|
||||
if project.kcl_file_count == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
projects.push(project);
|
||||
}
|
||||
|
||||
Ok(projects)
|
||||
@ -150,11 +157,14 @@ impl Configuration {
|
||||
return Err(anyhow::anyhow!("Project path is not a directory: {}", project_path));
|
||||
}
|
||||
|
||||
let walked = crate::settings::utils::walk_dir(project_dir).await?;
|
||||
|
||||
let mut project = crate::settings::types::file::Project {
|
||||
file: crate::settings::utils::walk_dir(project_dir).await?,
|
||||
file: walked.clone(),
|
||||
metadata: Some(tokio::fs::metadata(&project_dir).await?.into()),
|
||||
kcl_file_count: 0,
|
||||
directory_count: 0,
|
||||
default_file: crate::settings::types::file::get_default_kcl_file_for_dir(project_dir, walked).await?,
|
||||
};
|
||||
|
||||
// Populate the number of KCL files in the project.
|
||||
|
@ -33,6 +33,16 @@ pub async fn walk_dir<P>(dir: P) -> Result<FileEntry>
|
||||
where
|
||||
P: AsRef<Path> + Send,
|
||||
{
|
||||
// Make sure the path is a directory.
|
||||
if !dir.as_ref().is_dir() {
|
||||
return Err(anyhow::anyhow!("Path `{}` is not a directory", dir.as_ref().display()));
|
||||
}
|
||||
|
||||
// Make sure the directory exists.
|
||||
if !dir.as_ref().exists() {
|
||||
return Err(anyhow::anyhow!("Directory `{}` does not exist", dir.as_ref().display()));
|
||||
}
|
||||
|
||||
let mut entry = FileEntry {
|
||||
name: dir
|
||||
.as_ref()
|
||||
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
Before Width: | Height: | Size: 198 KiB After Width: | Height: | Size: 198 KiB |
Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 148 KiB |
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 102 KiB |
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 127 KiB |
@ -77,16 +77,6 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, uuid
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Enter edit mode.
|
||||
// We can't get control points of an existing sketch without being in edit mode.
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
SourceRange::default(),
|
||||
ModelingCmd::EditModeEnter { target: sketch_id },
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok((ctx, program, sketch_id))
|
||||
}
|
||||
|
||||
|