22import * as vscode from 'vscode' ;
33import * as path from 'path' ;
44import { l10n } from 'vscode' ;
5+ import * as fs from 'fs' ;
56
67// 置換文字のデコレーション設定
78const mojibakeDecoration = vscode . window . createTextEditorDecorationType ( {
@@ -32,6 +33,54 @@ const DEFAULT_EXCLUDE_PATTERNS = [
3233 '**/out/**'
3334] ;
3435
36+ // エラーコード定義
37+ const ERROR_CODE = 'E001' ;
38+ const ERROR_DESCRIPTION = 'U+FFFD (replacement character) detected' ;
39+
40+ // レポート出力インターフェース
41+ interface MojibakeReport {
42+ errorCode : string ;
43+ filePath : string ;
44+ line : number ;
45+ column : number ;
46+ }
47+
48+ // レポートファイル出力関数
49+ async function writeReport ( reports : MojibakeReport [ ] , reportPath : string ) {
50+ const header = `# Mojibake Inspector Report
51+ # Error Codes:
52+ # ${ ERROR_CODE } \t${ ERROR_DESCRIPTION } \n` ;
53+
54+ let content = '' ;
55+ if ( reports . length === 0 ) {
56+ content = 'No mojibake characters were found.\n' ;
57+ } else {
58+ content = 'ErrorCode\tFilePath\tLine\tColumn\n' +
59+ reports . map ( report =>
60+ `${ report . errorCode } \t${ report . filePath } \t${ report . line } \t${ report . column } `
61+ ) . join ( '\n' ) ;
62+ }
63+
64+ try {
65+ await vscode . workspace . fs . writeFile (
66+ vscode . Uri . file ( reportPath ) ,
67+ Buffer . from ( header + content , 'utf8' )
68+ ) ;
69+ vscode . window . showInformationMessage ( l10n . t ( 'Report saved to: {0}' , reportPath ) ) ;
70+ } catch ( error : unknown ) {
71+ const errorMessage = error instanceof Error ? error . message : String ( error ) ;
72+ vscode . window . showErrorMessage ( l10n . t ( 'Failed to save report: {0}' , errorMessage ) ) ;
73+ }
74+ }
75+
76+ // 既存のインターフェース定義の近くに追加
77+ interface MojibakeResult {
78+ file : string ;
79+ line : number ;
80+ mojibake : string ;
81+ encoding : string ;
82+ }
83+
3584export function activate ( context : vscode . ExtensionContext ) {
3685 try {
3786 console . log ( 'Activating Mojibake Inspector extension...' ) ;
@@ -156,6 +205,11 @@ async function findReplacementCharactersInWorkspace() {
156205 // ワークスペースの設定から除外パターンを取得(設定がない場合はデフォルトを使用)
157206 const config = vscode . workspace . getConfiguration ( 'mojibakeInspector' ) ;
158207 const excludePatterns = config . get < string [ ] > ( 'excludePatterns' , DEFAULT_EXCLUDE_PATTERNS ) ;
208+ const reportConfig = {
209+ enabled : config . get < boolean > ( 'report.enabled' , false ) ,
210+ outputPath : config . get < string > ( 'report.outputPath' , 'mojibake-report.txt' )
211+ } ;
212+ const reports : MojibakeReport [ ] = [ ] ;
159213
160214 const progress = await vscode . window . withProgress ( {
161215 location : vscode . ProgressLocation . Notification ,
@@ -193,6 +247,20 @@ async function findReplacementCharactersInWorkspace() {
193247 diagnostic . code = 'mojibake' ;
194248 diagnostics . push ( diagnostic ) ;
195249
250+ if ( reportConfig . enabled ) {
251+ const workspaceFolder = vscode . workspace . getWorkspaceFolder ( document . uri ) ;
252+ const relativePath = workspaceFolder
253+ ? path . relative ( workspaceFolder . uri . fsPath , document . uri . fsPath )
254+ : document . uri . fsPath ;
255+
256+ reports . push ( {
257+ errorCode : ERROR_CODE ,
258+ filePath : relativePath ,
259+ line : startPos . line + 1 ,
260+ column : startPos . character + 1
261+ } ) ;
262+ }
263+
196264 totalCount ++ ;
197265 }
198266
@@ -211,6 +279,15 @@ async function findReplacementCharactersInWorkspace() {
211279 return totalCount ;
212280 } ) ;
213281
282+ // レポートファイルの出力
283+ if ( reportConfig . enabled ) {
284+ const workspaceFolder = vscode . workspace . workspaceFolders ?. [ 0 ] ;
285+ if ( workspaceFolder ) {
286+ const reportPath = path . join ( workspaceFolder . uri . fsPath , reportConfig . outputPath ) ;
287+ await writeReport ( reports , reportPath ) ;
288+ }
289+ }
290+
214291 // 結果を表示
215292 if ( progress > 0 ) {
216293 vscode . window . showInformationMessage (
0 commit comments