@@ -32,71 +32,161 @@ export const build_commit_message_prompt = async (
3232 let changes_content = ''
3333 let context_content = ''
3434
35- for ( const file_diff_content of file_diffs ) {
36- const full_file_diff = 'diff --git ' + file_diff_content
37- const lines = full_file_diff . split ( '\n' )
38- const old_path_line = lines . find ( ( l ) => l . startsWith ( '--- a/' ) )
39- const new_path_line = lines . find ( ( l ) => l . startsWith ( '+++ b/' ) )
40- const rename_from_line = lines . find ( ( l ) => l . startsWith ( 'rename from ' ) )
41-
42- const old_path = old_path_line
43- ? old_path_line . substring ( '--- a/' . length )
44- : undefined
45- const new_path = new_path_line
46- ? new_path_line . substring ( '+++ b/' . length )
47- : undefined
48-
49- let final_diff_content = full_file_diff
50- let file_path : string | undefined
51- let is_deleted = false
52- const is_binary = lines . some ( ( l ) => l . startsWith ( 'Binary files ' ) )
53-
54- if ( new_path && new_path !== '/dev/null' ) {
55- file_path = new_path
56- } else if ( old_path && old_path !== '/dev/null' ) {
57- file_path = old_path
58- is_deleted = true
59- // Shorten diff of a deleted file
60- const split_diff = full_file_diff . split ( '+++ /dev/null' )
61- final_diff_content = split_diff [ 0 ] + '+++ /dev/null'
62- } else {
63- // Fallback: try to identify file from diff header (e.g. binary files)
64- // diff --git a/path b/path
65- const diff_header = lines [ 0 ]
66- const match = diff_header . match ( / ^ d i f f - - g i t a \/ ( .* ) b \/ ( .* ) $ / )
67- if ( match ) {
68- file_path = match [ 2 ]
69- }
70- }
71-
72- if ( file_path ) {
73- let status : 'created' | 'deleted' | 'renamed' | 'updated' = 'updated'
74-
75- if ( is_deleted ) {
76- status = 'deleted'
77- } else if ( ! old_path && new_path ) {
78- status = 'created'
79- } else if ( rename_from_line ) {
80- status = 'renamed'
81- } else if ( lines . some ( ( l ) => l . startsWith ( 'new file mode' ) ) ) {
82- status = 'created'
83- } else if ( lines . some ( ( l ) => l . startsWith ( 'deleted file mode' ) ) ) {
84- status = 'deleted'
35+ const diff_results = await Promise . all (
36+ file_diffs . map ( async ( file_diff_content ) => {
37+ let changes_content_part = ''
38+ let context_content_part = ''
39+
40+ const full_file_diff = 'diff --git ' + file_diff_content
41+ const lines = full_file_diff . split ( '\n' )
42+ const old_path_line = lines . find ( ( l ) => l . startsWith ( '--- a/' ) )
43+ const new_path_line = lines . find ( ( l ) => l . startsWith ( '+++ b/' ) )
44+ const rename_from_line = lines . find ( ( l ) => l . startsWith ( 'rename from ' ) )
45+
46+ const old_path = old_path_line
47+ ? old_path_line . substring ( '--- a/' . length )
48+ : undefined
49+ const new_path = new_path_line
50+ ? new_path_line . substring ( '+++ b/' . length )
51+ : undefined
52+
53+ let final_diff_content = full_file_diff
54+ let file_path : string | undefined
55+ let is_deleted = false
56+ const is_binary = lines . some ( ( l ) => l . startsWith ( 'Binary files ' ) )
57+
58+ if ( new_path && new_path !== '/dev/null' ) {
59+ file_path = new_path
60+ } else if ( old_path && old_path !== '/dev/null' ) {
61+ file_path = old_path
8562 is_deleted = true
63+ const split_diff = full_file_diff . split ( '+++ /dev/null' )
64+ final_diff_content = split_diff [ 0 ] + '+++ /dev/null'
65+ } else {
66+ const diff_header = lines [ 0 ]
67+ const match = diff_header . match ( / ^ d i f f - - g i t a \/ ( .* ) b \/ ( .* ) $ / )
68+ if ( match ) {
69+ file_path = match [ 2 ]
70+ }
8671 }
8772
88- let old_path_attr : string | undefined
89- if ( status === 'renamed' ) {
90- if ( rename_from_line ) {
91- old_path_attr = rename_from_line
92- . substring ( 'rename from ' . length )
93- . trim ( )
73+ if ( file_path ) {
74+ let status : 'created' | 'deleted' | 'renamed' | 'updated' = 'updated'
75+
76+ if ( is_deleted ) {
77+ status = 'deleted'
78+ } else if ( ! old_path && new_path ) {
79+ status = 'created'
80+ } else if ( rename_from_line ) {
81+ status = 'renamed'
82+ } else if ( lines . some ( ( l ) => l . startsWith ( 'new file mode' ) ) ) {
83+ status = 'created'
84+ } else if ( lines . some ( ( l ) => l . startsWith ( 'deleted file mode' ) ) ) {
85+ status = 'deleted'
86+ is_deleted = true
87+ }
88+
89+ let old_path_attr : string | undefined
90+ if ( status === 'renamed' ) {
91+ if ( rename_from_line ) {
92+ old_path_attr = rename_from_line
93+ . substring ( 'rename from ' . length )
94+ . trim ( )
95+ }
96+ }
97+
98+ let is_ignored = false
99+
100+ if ( file_path && workspace_provider ) {
101+ const absolute_path = vscode . Uri . joinPath (
102+ repository . rootUri ,
103+ file_path
104+ ) . fsPath
105+ if (
106+ workspace_provider . is_ignored_by_patterns ( absolute_path ) ||
107+ workspace_provider . is_excluded ( file_path )
108+ ) {
109+ is_ignored = true
110+ }
111+ }
112+
113+ if ( is_binary ) {
114+ if ( status === 'created' )
115+ final_diff_content = DIFF_PLACEHOLDERS . BINARY_FILE_CREATED
116+ else if ( status === 'deleted' )
117+ final_diff_content = DIFF_PLACEHOLDERS . BINARY_FILE_DELETED
118+ else final_diff_content = DIFF_PLACEHOLDERS . BINARY_FILE_MODIFIED
119+ } else if ( is_ignored ) {
120+ if ( status === 'created' )
121+ final_diff_content = DIFF_PLACEHOLDERS . FILE_CREATED
122+ else if ( status === 'deleted' )
123+ final_diff_content = DIFF_PLACEHOLDERS . FILE_DELETED
124+ else if ( status === 'renamed' )
125+ final_diff_content = DIFF_PLACEHOLDERS . FILE_RENAMED
126+ else final_diff_content = DIFF_PLACEHOLDERS . FILE_MODIFIED
127+ } else {
128+ const hunk_start_index = final_diff_content . indexOf ( '\n@@ ' )
129+ if ( hunk_start_index !== - 1 ) {
130+ final_diff_content = final_diff_content . substring (
131+ hunk_start_index + 1
132+ )
133+ } else if ( ! final_diff_content . startsWith ( '@@ ' ) ) {
134+ final_diff_content = ''
135+ }
136+ }
137+
138+ const should_include_full_content = ! is_ignored
139+
140+ if (
141+ ! is_deleted &&
142+ file_path &&
143+ ! is_binary &&
144+ should_include_full_content
145+ ) {
146+ try {
147+ let full_content = ''
148+ try {
149+ full_content = await repository . show ( '' , file_path )
150+ } catch ( e ) {
151+ const uri = vscode . Uri . joinPath ( repository . rootUri , file_path )
152+ const content = await vscode . workspace . fs . readFile ( uri )
153+ if ( ! content . includes ( 0 ) ) {
154+ full_content = Buffer . from ( content ) . toString ( 'utf8' )
155+ }
156+ }
157+ if ( full_content && ! full_content . includes ( '\0' ) ) {
158+ const full_content_tokens = Math . ceil ( full_content . length / 4 )
159+ if ( full_content_tokens <= MAX_FILE_TOKENS_FOR_COMMIT_MESSAGE ) {
160+ context_content_part = PromptBuilder . build_file_context ( {
161+ filepath : file_path ,
162+ content : full_content ,
163+ is_binary : false
164+ } )
165+ }
166+ }
167+ } catch ( err ) { }
94168 }
169+
170+ changes_content_part = PromptBuilder . build_diff_file_context ( {
171+ status,
172+ filepath : file_path ,
173+ old_filepath : old_path_attr ,
174+ diff_content : final_diff_content
175+ } )
95176 }
96177
97- let is_ignored = false
178+ return { changes_content_part, context_content_part }
179+ } )
180+ )
98181
99- if ( file_path && workspace_provider ) {
182+ for ( const res of diff_results ) {
183+ if ( res . context_content_part ) context_content += res . context_content_part
184+ if ( res . changes_content_part ) changes_content += res . changes_content_part
185+ }
186+
187+ const context_results = await Promise . all (
188+ context_files . map ( async ( file_path ) => {
189+ if ( workspace_provider ) {
100190 const absolute_path = vscode . Uri . joinPath (
101191 repository . rootUri ,
102192 file_path
@@ -105,105 +195,32 @@ export const build_commit_message_prompt = async (
105195 workspace_provider . is_ignored_by_patterns ( absolute_path ) ||
106196 workspace_provider . is_excluded ( file_path )
107197 ) {
108- is_ignored = true
109- }
110- }
111-
112- if ( is_binary ) {
113- if ( status === 'created' )
114- final_diff_content = DIFF_PLACEHOLDERS . BINARY_FILE_CREATED
115- else if ( status === 'deleted' )
116- final_diff_content = DIFF_PLACEHOLDERS . BINARY_FILE_DELETED
117- else final_diff_content = DIFF_PLACEHOLDERS . BINARY_FILE_MODIFIED
118- } else if ( is_ignored ) {
119- if ( status === 'created' )
120- final_diff_content = DIFF_PLACEHOLDERS . FILE_CREATED
121- else if ( status === 'deleted' )
122- final_diff_content = DIFF_PLACEHOLDERS . FILE_DELETED
123- else if ( status === 'renamed' )
124- final_diff_content = DIFF_PLACEHOLDERS . FILE_RENAMED
125- else final_diff_content = DIFF_PLACEHOLDERS . FILE_MODIFIED
126- } else {
127- const hunk_start_index = final_diff_content . indexOf ( '\n@@ ' )
128- if ( hunk_start_index !== - 1 ) {
129- final_diff_content = final_diff_content . substring (
130- hunk_start_index + 1
131- )
132- } else if ( ! final_diff_content . startsWith ( '@@ ' ) ) {
133- final_diff_content = ''
198+ return ''
134199 }
135200 }
136201
137- const should_include_full_content = ! is_ignored
138-
139- if (
140- ! is_deleted &&
141- file_path &&
142- ! is_binary &&
143- should_include_full_content
144- ) {
145- try {
146- let full_content = ''
147- try {
148- full_content = await repository . show ( '' , file_path )
149- } catch ( e ) {
150- const uri = vscode . Uri . joinPath ( repository . rootUri , file_path )
151- const content = await vscode . workspace . fs . readFile ( uri )
152- if ( ! content . includes ( 0 ) ) {
153- full_content = Buffer . from ( content ) . toString ( 'utf8' )
154- }
155- }
156- if ( full_content && ! full_content . includes ( '\0' ) ) {
157- const full_content_tokens = Math . ceil ( full_content . length / 4 )
158- if ( full_content_tokens <= MAX_FILE_TOKENS_FOR_COMMIT_MESSAGE ) {
159- context_content += PromptBuilder . build_file_context ( {
160- filepath : file_path ,
161- content : full_content ,
162- is_binary : false
163- } )
164- }
202+ try {
203+ const uri = vscode . Uri . joinPath ( repository . rootUri , file_path )
204+ const content_uint8 = await vscode . workspace . fs . readFile ( uri )
205+ if ( ! content_uint8 . includes ( 0 ) ) {
206+ const full_content = Buffer . from ( content_uint8 ) . toString ( 'utf8' )
207+ const full_content_tokens = Math . ceil ( full_content . length / 4 )
208+ if ( full_content_tokens <= MAX_FILE_TOKENS_FOR_COMMIT_MESSAGE ) {
209+ return PromptBuilder . build_file_context ( {
210+ filepath : file_path ,
211+ content : full_content ,
212+ is_binary : false
213+ } )
165214 }
166- } catch ( err ) { }
167- }
215+ }
216+ } catch ( err ) { }
168217
169- changes_content += PromptBuilder . build_diff_file_context ( {
170- status,
171- filepath : file_path ,
172- old_filepath : old_path_attr ,
173- diff_content : final_diff_content
174- } )
175- }
176- }
218+ return ''
219+ } )
220+ )
177221
178- for ( const file_path of context_files ) {
179- if ( workspace_provider ) {
180- const absolute_path = vscode . Uri . joinPath (
181- repository . rootUri ,
182- file_path
183- ) . fsPath
184- if (
185- workspace_provider . is_ignored_by_patterns ( absolute_path ) ||
186- workspace_provider . is_excluded ( file_path )
187- ) {
188- continue
189- }
190- }
191-
192- try {
193- const uri = vscode . Uri . joinPath ( repository . rootUri , file_path )
194- const content_uint8 = await vscode . workspace . fs . readFile ( uri )
195- if ( ! content_uint8 . includes ( 0 ) ) {
196- const full_content = Buffer . from ( content_uint8 ) . toString ( 'utf8' )
197- const full_content_tokens = Math . ceil ( full_content . length / 4 )
198- if ( full_content_tokens <= MAX_FILE_TOKENS_FOR_COMMIT_MESSAGE ) {
199- context_content += PromptBuilder . build_file_context ( {
200- filepath : file_path ,
201- content : full_content ,
202- is_binary : false
203- } )
204- }
205- }
206- } catch ( err ) { }
222+ for ( const res of context_results ) {
223+ if ( res ) context_content += res
207224 }
208225
209226 let prompt_sections = ''
0 commit comments