diff --git a/.zed/settings.json b/.zed/settings.json index 76745b819..5a5ca963d 100644 --- a/.zed/settings.json +++ b/.zed/settings.json @@ -6,7 +6,7 @@ "lsp": { "rust-analyzer": { "initialization_options": { - "checkOnSave": { + "check": { "command": "clippy" } } diff --git a/crates/tinymist-query/src/analysis/completion/typst_specific.rs b/crates/tinymist-query/src/analysis/completion/typst_specific.rs index e7df5cb2e..34310de39 100644 --- a/crates/tinymist-query/src/analysis/completion/typst_specific.rs +++ b/crates/tinymist-query/src/analysis/completion/typst_specific.rs @@ -30,12 +30,14 @@ impl CompletionPair<'_, '_, '_> { /// Add completions for all available packages. pub fn package_completions(&mut self, all_versions: bool) { let w = self.worker.world().clone(); + // Finds packages that are in `@preview` let mut packages = w.packages().iter().collect_vec(); - // local_packages to references and add them to the packages + + // Finds packages that are not in `@preview` #[cfg(feature = "local-registry")] - let local_packages_refs = self.worker.ctx.local_packages(); + let other_packages_refs = self.worker.ctx.non_preview_packages(); #[cfg(feature = "local-registry")] - packages.extend(local_packages_refs.iter()); + packages.extend(other_packages_refs.iter()); packages.sort_by_key(|entry| { ( diff --git a/crates/tinymist-query/src/analysis/global.rs b/crates/tinymist-query/src/analysis/global.rs index 3eacef5b3..11b9822c8 100644 --- a/crates/tinymist-query/src/analysis/global.rs +++ b/crates/tinymist-query/src/analysis/global.rs @@ -689,10 +689,16 @@ impl SharedContext { self.world().font_resolver.describe_font(&font) } - /// Gets the local packages and their descriptions. - pub fn local_packages(&self) -> EcoVec { + /// Gets the packages other than that in the preview namespace and their + /// descriptions. + pub fn non_preview_packages(&self) -> EcoVec { #[cfg(feature = "local-registry")] - let it = || crate::package::list_package(self.world(), None); + let it = || { + crate::package::list_package( + self.world(), + crate::package::PackageFilter::ExceptFor(EcoString::inline("preview")), + ) + }; #[cfg(not(feature = "local-registry"))] let it = || Default::default(); self.analysis.local_packages.lock().get_or_init(it).clone() diff --git a/crates/tinymist-query/src/hover.rs b/crates/tinymist-query/src/hover.rs index 2cb6a4438..afcf87235 100644 --- a/crates/tinymist-query/src/hover.rs +++ b/crates/tinymist-query/src/hover.rs @@ -259,9 +259,9 @@ impl HoverWorker<'_> { .filter(|it| it.matches_versionless(&versionless_spec)), ); } - // local_packages to references and add them to the packages + // Add non-preview packages #[cfg(feature = "local-registry")] - let local_packages = self.ctx.local_packages(); + let local_packages = self.ctx.non_preview_packages(); #[cfg(feature = "local-registry")] if !package_spec.is_preview() { packages.extend( diff --git a/crates/tinymist-query/src/package.rs b/crates/tinymist-query/src/package.rs index e66df4f92..365c390a6 100644 --- a/crates/tinymist-query/src/package.rs +++ b/crates/tinymist-query/src/package.rs @@ -77,11 +77,22 @@ pub fn check_package(ctx: &mut LocalContext, spec: &PackageInfo) -> StrResult<() Ok(()) } +/// A filter for packages. +#[cfg(feature = "local-registry")] +pub enum PackageFilter { + /// Filter for packages that match the given namespace. + For(EcoString), + /// Filter for packages that do not match the given namespace. + ExceptFor(EcoString), + /// Filter that matches all packages. + All, +} + #[cfg(feature = "local-registry")] /// Get the packages in namespaces and their descriptions. pub fn list_package( world: &tinymist_project::LspWorld, - ns: Option, + filter: PackageFilter, ) -> EcoVec { trait IsDirFollowLinks { fn is_dir_follow_links(&self) -> bool; @@ -104,10 +115,8 @@ pub fn list_package( // intended for storage of local packages. let mut packages = eco_vec![]; - log::info!( - "searching for packages in namespace {ns:?} in paths {:?}", - registry.paths() - ); + let paths = registry.paths(); + log::info!("searching for packages in paths {paths:?}"); let mut search_in_dir = |local_path: PathBuf, ns: EcoString| { if !local_path.exists() || !local_path.is_dir_follow_links() { @@ -179,22 +188,34 @@ pub fn list_package( } }; - for dir in registry.paths() { - if let Some(ns) = &ns { - let local_path = dir.join(ns.as_str()); - search_in_dir(local_path, ns.clone()); - } else { - let Some(namespaces) = once_log(std::fs::read_dir(dir), "read package directory") - else { + for dir in paths { + let matching_ns = match &filter { + PackageFilter::For(ns) => { + let local_path = dir.join(ns.as_str()); + search_in_dir(local_path, ns.clone()); + + continue; + } + PackageFilter::ExceptFor(ns) => Some(ns), + PackageFilter::All => None, + }; + + let Some(namespaces) = once_log(std::fs::read_dir(dir), "read package directory") else { + continue; + }; + for dir in namespaces { + let Some(dir) = once_log(dir, "read ns directory") else { continue; }; - for dir in namespaces { - let Some(dir) = once_log(dir, "read ns directory") else { - continue; - }; - let local_path = dir.path(); - search_in_dir(local_path, dir.file_name().to_string_lossy().into()); + let ns = dir.file_name(); + let ns = ns.to_string_lossy(); + if let Some(matching_ns) = &matching_ns + && matching_ns.as_str() == ns.as_ref() + { + continue; } + let local_path = dir.path(); + search_in_dir(local_path, ns.into()); } } diff --git a/crates/tinymist/src/cmd.rs b/crates/tinymist/src/cmd.rs index 488b1586e..6e87447f7 100644 --- a/crates/tinymist/src/cmd.rs +++ b/crates/tinymist/src/cmd.rs @@ -484,10 +484,13 @@ impl ServerState { let snap = self.snapshot().map_err(internal_error)?; just_future(async move { - let packages = tinymist_query::package::list_package(snap.world(), Some(ns)) - .into_iter() - .map(PackageInfo::from) - .collect::>(); + let packages = tinymist_query::package::list_package( + snap.world(), + tinymist_query::package::PackageFilter::For(ns), + ) + .into_iter() + .map(PackageInfo::from) + .collect::>(); serde_json::to_value(packages).map_err(|e| internal_error(e.to_string())) }) diff --git a/editors/vscode/src/ui-extends.ts b/editors/vscode/src/ui-extends.ts index f9ce53033..48452b1d2 100644 --- a/editors/vscode/src/ui-extends.ts +++ b/editors/vscode/src/ui-extends.ts @@ -63,6 +63,7 @@ ${cjkChars} CJK ${plural("Character", cjkChars)} const formatString = statusBarFormatString() .replace(/\{wordCount\}/g, `${words} ${plural("Word", words)}`) + .replace(/\{charCount\}/g, `${chars} ${plural("Character", chars)}`) .replace(/\{pageCount\}/g, `${pages} ${plural("Page", pages)}`) .replace(/\{fileName\}/g, fileNameWithoutExt); diff --git a/locales/tinymist-vscode.toml b/locales/tinymist-vscode.toml index 3917c115b..f234dfbe1 100644 --- a/locales/tinymist-vscode.toml +++ b/locales/tinymist-vscode.toml @@ -1184,8 +1184,8 @@ en = "Status Bar Format" zh = "状态栏格式" [extension.tinymist.config.tinymist.statusBarFormat.desc] -en = "Set format string of the server status. For example, `{compileStatusIcon}{wordCount} [{fileName}]` will format the status as `$(check) 123 words [main]`. Valid placeholders are:\n\n- `{compileStatusIcon}`: Icon indicating the compile status\n- `{wordCount}`: Number of words in the document\n- `{fileName}`: Name of the file being compiled\n\nNote: The status bar will be hidden if the format string is empty." -zh = "设置服务器状态的格式字符串。例如,`{compileStatusIcon}{wordCount} [{fileName}]` 将格式化状态为 `$(check) 123 words [main]`。有效的占位符包括:\n\n- `{compileStatusIcon}`:指示编译状态的图标\n- `{wordCount}`:文档中的字数\n- `{fileName}`:正在编译的文件的名称\n\n注意:如果格式字符串为空,则状态栏将被隐藏。" +en = "Set format string of the server status. For example, `{compileStatusIcon}{wordCount} [{fileName}]` will format the status as `$(check) 123 words [main]`. Valid placeholders are:\n\n- `{compileStatusIcon}`: Icon indicating the compile status\n- `{wordCount}`: Number of words in the document\n- `{charCount}`: Number of characters in the document\n- `{fileName}`: Name of the file being compiled\n\nNote: The status bar will be hidden if the format string is empty." +zh = "设置服务器状态的格式字符串。例如,`{compileStatusIcon}{wordCount} [{fileName}]` 将格式化状态为 `$(check) 123 words [main]`。有效的占位符包括:\n\n- `{compileStatusIcon}`:指示编译状态的图标\n- `{wordCount}`:文档中的字数\n- `{charCount}`:文档中的字符数\n- `{fileName}`:正在编译的文件的名称\n\n注意:如果格式字符串为空,则状态栏将被隐藏。" [extension.tinymist.config.tinymist.typstExtraArgs.title] en = "Typst Extra Arguments"