Skip to content

Commit 578f915

Browse files
authored
Merge branch 'main' into feature/stdout-quiet-output
2 parents e171052 + cc2eb20 commit 578f915

File tree

14 files changed

+927
-111
lines changed

14 files changed

+927
-111
lines changed

Cargo.lock

Lines changed: 57 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ colored = "2.1.0"
1414
indicatif = "0.17.8"
1515
log = "0.4"
1616
num-format = { version = "0.4.4", features = ["with-system-locale"] }
17+
serde = { version = "1.0", features = ["derive"] }
1718
serde_json = "1.0.114"
1819
handlebars = "4.3"
1920
jwalk = "0.8"
@@ -35,3 +36,5 @@ env_logger = { version = "0.11.3" }
3536
arboard = { version = "3.4.0" }
3637
derive_builder = { version = "0.20.2" }
3738
winapi = { version = "0.3.9", features = ["errhandlingapi"] }
39+
unicode-width = "0.1"
40+
terminal_size = "0.3"

crates/code2prompt-core/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ colored = { workspace = true }
2727
indicatif = { workspace = true }
2828
log = { workspace = true }
2929
num-format = { workspace = true }
30+
serde = { workspace = true }
3031
serde_json = { workspace = true }
3132
handlebars = { workspace = true }
3233
jwalk = { workspace = true }

crates/code2prompt-core/build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ fn main() {
55
println!("cargo:rustc-link-arg=-undefined");
66
println!("cargo:rustc-link-arg=dynamic_lookup");
77
}
8-
}
8+
}

crates/code2prompt-core/src/configuration.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ pub struct Code2PromptConfig {
101101
/// Extra template data
102102
#[builder(default)]
103103
pub user_variables: HashMap<String, String>,
104+
105+
/// If true, token counting will be performed for each file (for token map display)
106+
#[builder(default)]
107+
pub token_map_enabled: bool,
104108
}
105109

106110
impl Code2PromptConfig {

crates/code2prompt-core/src/filter.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub fn build_globset(patterns: &[String]) -> GlobSet {
2626
} else {
2727
patterns.to_vec()
2828
};
29-
29+
3030
for pattern in expanded_patterns {
3131
// If the pattern does not contain a '/' or the platform’s separator, prepend "**/"
3232
let normalized_pattern =
@@ -118,4 +118,4 @@ fn expand_brace_patterns(patterns: &[String]) -> Vec<String> {
118118
.split(',')
119119
.map(|expanded_pattern| format!("{}/{}", common_prefix, expanded_pattern))
120120
.collect::<Vec<String>>();
121-
}
121+
}

crates/code2prompt-core/src/path.rs

Lines changed: 78 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,32 @@
22
use crate::configuration::Code2PromptConfig;
33
use crate::filter::{build_globset, should_include_file};
44
use crate::sort::{sort_files, sort_tree, FileSortMethod};
5+
use crate::tokenizer::count_tokens;
56
use crate::util::strip_utf8_bom;
67
use anyhow::Result;
78
use ignore::WalkBuilder;
89
use log::debug;
10+
use serde::{Deserialize, Serialize};
911
use serde_json::json;
1012
use std::fs;
1113
use std::path::Path;
1214
use termtree::Tree;
1315

16+
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
17+
pub struct EntryMetadata {
18+
pub is_dir: bool,
19+
pub is_symlink: bool,
20+
}
21+
22+
impl From<&std::fs::Metadata> for EntryMetadata {
23+
fn from(meta: &std::fs::Metadata) -> Self {
24+
Self {
25+
is_dir: meta.is_dir(),
26+
is_symlink: meta.is_symlink(),
27+
}
28+
}
29+
}
30+
1431
/// Traverses the directory and returns the string representation of the tree and the vector of JSON file representations.
1532
///
1633
/// This function uses the provided configuration to determine which files to include, how to format them,
@@ -77,56 +94,72 @@ pub fn traverse_directory(config: &Code2PromptConfig) -> Result<(String, Vec<ser
7794

7895
// ~~~ Processing File ~~~
7996
if path.is_file() && file_match {
80-
if let Ok(code_bytes) = fs::read(path) {
81-
let clean_bytes = strip_utf8_bom(&code_bytes);
82-
let code = String::from_utf8_lossy(&clean_bytes);
83-
84-
let code_block = wrap_code_block(
85-
&code,
86-
path.extension().and_then(|ext| ext.to_str()).unwrap_or(""),
87-
config.line_numbers,
88-
config.no_codeblock,
89-
);
90-
91-
if !code.trim().is_empty() && !code.contains(char::REPLACEMENT_CHARACTER) {
92-
// ~~~ Filepath ~~~
93-
let file_path = if config.absolute_path {
94-
path.display().to_string()
95-
} else {
96-
format!("{}/{}", parent_directory, relative_path.display())
97-
};
98-
99-
// ~~~ File JSON Representation ~~~
100-
let mut file_entry = serde_json::Map::new();
101-
file_entry.insert("path".to_string(), json!(file_path));
102-
file_entry.insert(
103-
"extension".to_string(),
104-
json!(path.extension().and_then(|ext| ext.to_str()).unwrap_or("")),
97+
if let Ok(metadata) = entry.metadata() {
98+
if let Ok(code_bytes) = fs::read(path) {
99+
let clean_bytes = strip_utf8_bom(&code_bytes);
100+
let code = String::from_utf8_lossy(&clean_bytes);
101+
102+
let code_block = wrap_code_block(
103+
&code,
104+
path.extension().and_then(|ext| ext.to_str()).unwrap_or(""),
105+
config.line_numbers,
106+
config.no_codeblock,
105107
);
106-
file_entry.insert("code".to_string(), json!(code_block));
107-
108-
// If date sorting is requested, record the file modification time.
109-
if let Some(method) = config.sort_method {
110-
if method == FileSortMethod::DateAsc
111-
|| method == FileSortMethod::DateDesc
112-
{
113-
let mod_time = fs::metadata(path)
114-
.and_then(|m| m.modified())
115-
.and_then(|mtime| {
116-
Ok(mtime.duration_since(std::time::SystemTime::UNIX_EPOCH))
117-
})
118-
.map(|d| d.unwrap().as_secs())
119-
.unwrap_or(0);
120-
file_entry.insert("mod_time".to_string(), json!(mod_time));
108+
109+
if !code.trim().is_empty() && !code.contains(char::REPLACEMENT_CHARACTER) {
110+
// ~~~ Filepath ~~~
111+
let file_path = if config.absolute_path {
112+
path.to_string_lossy().to_string()
113+
} else {
114+
relative_path.to_string_lossy().to_string()
115+
};
116+
117+
// ~~~ File JSON Representation ~~~
118+
let mut file_entry = serde_json::Map::new();
119+
file_entry.insert("path".to_string(), json!(file_path));
120+
file_entry.insert(
121+
"extension".to_string(),
122+
json!(path.extension().and_then(|ext| ext.to_str()).unwrap_or("")),
123+
);
124+
file_entry.insert("code".to_string(), json!(code_block));
125+
126+
// Store metadata
127+
let entry_meta = EntryMetadata::from(&metadata);
128+
file_entry
129+
.insert("metadata".to_string(), serde_json::to_value(entry_meta)?);
130+
131+
// Add token count for the file only if token map is enabled
132+
if config.token_map_enabled {
133+
let token_count = count_tokens(&code, &config.encoding);
134+
file_entry.insert("token_count".to_string(), json!(token_count));
121135
}
136+
137+
// If date sorting is requested, record the file modification time.
138+
if let Some(method) = config.sort_method {
139+
if method == FileSortMethod::DateAsc
140+
|| method == FileSortMethod::DateDesc
141+
{
142+
let mod_time = metadata
143+
.modified()
144+
.ok()
145+
.and_then(|mtime| {
146+
mtime
147+
.duration_since(std::time::SystemTime::UNIX_EPOCH)
148+
.ok()
149+
})
150+
.map(|d| d.as_secs())
151+
.unwrap_or(0);
152+
file_entry.insert("mod_time".to_string(), json!(mod_time));
153+
}
154+
}
155+
files.push(serde_json::Value::Object(file_entry));
156+
debug!(target: "included_files", "Included file: {}", file_path);
157+
} else {
158+
debug!("Excluded file (empty or invalid UTF-8): {}", path.display());
122159
}
123-
files.push(serde_json::Value::Object(file_entry));
124-
debug!(target: "included_files", "Included file: {}", file_path);
125160
} else {
126-
debug!("Excluded file (empty or invalid UTF-8): {}", path.display());
161+
debug!("Failed to read file: {}", path.display());
127162
}
128-
} else {
129-
debug!("Failed to read file: {}", path.display());
130163
}
131164
}
132165
}

0 commit comments

Comments
 (0)