Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,15 @@
"group": "navigation@3"
},
{
"command": "todo-tree.scanWorkspaceAndOpenFiles",
"command": "todo-tree.scanGitBranch",
"when": "view =~ /todo-tree/ && todo-tree-scan-mode == 'current file' && todo-tree-show-scan-mode-button == true",
"group": "navigation@3"
},
{
"command": "todo-tree.scanWorkspaceAndOpenFiles",
"when": "view =~ /todo-tree/ && todo-tree-scan-mode == 'git branch' && todo-tree-show-scan-mode-button == true",
"group": "navigation@3"
},
{
"command": "todo-tree.scanWorkspaceOnly",
"when": "view =~ /todo-tree/ && todo-tree-scan-mode == 'workspace' && todo-tree-show-scan-mode-button == true",
Expand Down Expand Up @@ -221,6 +226,11 @@
"when": "view =~ /todo-tree/ && todo-tree-scan-mode != 'workspace only'",
"group": "3-view"
},
{
"command": "todo-tree.scanGitBranch",
"when": "view =~ /todo-tree/ && todo-tree-scan-mode != 'git branch'",
"group": "3-view"
},
{
"command": "todo-tree.expand",
"when": "view =~ /todo-tree/ && todo-tree-expanded == false",
Expand Down Expand Up @@ -395,6 +405,12 @@
"category": "%todo-tree.command.category%",
"icon": "$(folder)"
},
{
"command": "todo-tree.scanGitBranch",
"title": "%todo-tree.command.scanGitBranch.title%",
"category": "%todo-tree.command.category%",
"icon": "$(git-branch)"
},
{
"command": "todo-tree.addTag",
"title": "%todo-tree.command.addTag.title%",
Expand Down Expand Up @@ -1005,14 +1021,16 @@
"workspace",
"open files",
"current file",
"workspace only"
"workspace only",
"git branch"
],
"markdownDescription": "%todo-tree.configuration.tree.scanMode.markdownDescription%",
"markdownEnumDescriptions": [
"%todo-tree.configuration.tree.scanMode.markdownEnumDescriptions.1%",
"%todo-tree.configuration.tree.scanMode.markdownEnumDescriptions.2%",
"%todo-tree.configuration.tree.scanMode.markdownEnumDescriptions.3%",
"%todo-tree.configuration.tree.scanMode.markdownEnumDescriptions.4%"
"%todo-tree.configuration.tree.scanMode.markdownEnumDescriptions.4%",
"%todo-tree.configuration.tree.scanMode.markdownEnumDescriptions.5%"
],
"type": "string"
},
Expand Down
2 changes: 2 additions & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"todo-tree.command.scanCurrentFileOnly.title": "Scan Current File Only",
"todo-tree.command.scanWorkspaceAndOpenFiles.title": "Scan Workspace And Open Files",
"todo-tree.command.scanWorkspaceOnly.title": "Scan Workspace Only",
"todo-tree.command.scanGitBranch.title": "Scan Git Branch",
"todo-tree.command.addTag.title": "Add Tag",
"todo-tree.command.removeTag.title": "Remove Tag",
"todo-tree.command.exportTree.title": "Export Tree",
Expand Down Expand Up @@ -113,6 +114,7 @@
"todo-tree.configuration.tree.scanMode.markdownEnumDescriptions.2": "Scan open files only",
"todo-tree.configuration.tree.scanMode.markdownEnumDescriptions.3": "Scan the current file only",
"todo-tree.configuration.tree.scanMode.markdownEnumDescriptions.4": "Scan the workspace but don't refresh files open in the editor",
"todo-tree.configuration.tree.scanMode.markdownEnumDescriptions.5": "Scan only TODOs added in the current git branch (compared to main or master)",
"todo-tree.configuration.tree.showBadges.markdownDescription": "Show badges and SCM state in the tree view.",
"todo-tree.configuration.tree.showCountsInTree.markdownDescription": "Show counts of TODOs in the tree.",
"todo-tree.configuration.tree.showInExplorer.deprecationMessage": "This setting is no longer used. Please drag the view to move it.",
Expand Down
2 changes: 2 additions & 0 deletions package.nls.zh-cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"todo-tree.command.scanCurrentFileOnly.title": "仅扫描当前文件",
"todo-tree.command.scanWorkspaceAndOpenFiles.title": "扫描整个工作区和打开的文件",
"todo-tree.command.scanWorkspaceOnly.title": "仅扫描整个工作区",
"todo-tree.command.scanGitBranch.title": "扫描 Git 分支",
"todo-tree.command.addTag.title": "添加标签类型",
"todo-tree.command.removeTag.title": "移除标签类型",
"todo-tree.command.exportTree.title": "导出树状图",
Expand Down Expand Up @@ -98,6 +99,7 @@
"todo-tree.configuration.tree.scanMode.markdownEnumDescriptions.2": "仅扫描打开的文件。",
"todo-tree.configuration.tree.scanMode.markdownEnumDescriptions.3": "仅扫描当前文件。",
"todo-tree.configuration.tree.scanMode.markdownEnumDescriptions.4": "仅扫描整个工作区(不包括打开的文件)。",
"todo-tree.configuration.tree.scanMode.markdownEnumDescriptions.5": "仅扫描当前 Git 分支中添加的待办事项(与 main 或 master 分支比较)。",
"todo-tree.configuration.tree.showBadges.markdownDescription": "在树状图中显示 badges 和 SCM 的状态。",
"todo-tree.configuration.tree.showCountsInTree.markdownDescription": "在树状图中显示待办事项的数目。",
"todo-tree.configuration.tree.showInExplorer.deprecationMessage": "该设置不再使用,请拖动视图来进行移动。",
Expand Down
196 changes: 178 additions & 18 deletions src/extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ var SCAN_MODE_WORKSPACE_AND_OPEN_FILES = 'workspace';
var SCAN_MODE_OPEN_FILES = 'open files';
var SCAN_MODE_CURRENT_FILE = 'current file';
var SCAN_MODE_WORKSPACE_ONLY = 'workspace only';
var SCAN_MODE_GIT_BRANCH = 'git branch';

var STATUS_BAR_TOTAL = 'total';
var STATUS_BAR_TAGS = 'tags';
Expand Down Expand Up @@ -274,6 +275,10 @@ function activate( context )
{
statusBarIndicator.text += " (in current file)";
}
else if( scanMode === SCAN_MODE_GIT_BRANCH )
{
statusBarIndicator.text += " (in git branch)";
}

statusBarIndicator.command = "todo-tree.onStatusBarClicked";
}
Expand Down Expand Up @@ -350,6 +355,130 @@ function activate( context )
} );
}

function getBaseBranch( workspaceFolder )
{
return new Promise( function( resolve, reject )
{
// First try 'main'
child_process.exec( "git rev-parse --verify main", { cwd: workspaceFolder }, ( err, stdout, stderr ) =>
{
if( !err )
{
debug( "Using 'main' as base branch" );
resolve( 'main' );
}
else
{
// Fall back to 'master'
child_process.exec( "git rev-parse --verify master", { cwd: workspaceFolder }, ( err, stdout, stderr ) =>
{
if( !err )
{
debug( "Using 'master' as base branch" );
resolve( 'master' );
}
else
{
debug( "No main or master branch found" );
reject( new Error( "No 'main' or 'master' branch found" ) );
}
} );
}
} );
} );
}

function searchGitBranch( workspaceFolder )
{
return getBaseBranch( workspaceFolder ).then( function( baseBranch )
{
return new Promise( function( resolve, reject )
{
debug( "Running git diff " + baseBranch + "..HEAD in " + workspaceFolder );

child_process.exec( "git diff " + baseBranch + "..HEAD --unified=0", { cwd: workspaceFolder, maxBuffer: 50 * 1024 * 1024 }, ( err, stdout, stderr ) =>
{
if( err )
{
debug( "Git diff error: " + stderr );
reject( new Error( stderr || err.message ) );
return;
}

var diffOutput = stdout.toString();
if( !diffOutput.trim() )
{
debug( "No diff found between " + baseBranch + " and HEAD" );
resolve();
return;
}

var regex = utils.getRegexForEditorSearch( true );
var lines = diffOutput.split( '\n' );
var currentFile = null;
var currentLineNumber = 0;

for( var i = 0; i < lines.length; i++ )
{
var line = lines[ i ];

// Parse file header: +++ b/path/to/file
if( line.startsWith( '+++ b/' ) )
{
currentFile = line.substring( 6 );
debug( "Diff file: " + currentFile );
continue;
}

// Parse hunk header: @@ -old,count +new,count @@
var hunkMatch = line.match( /^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/ );
if( hunkMatch )
{
currentLineNumber = parseInt( hunkMatch[ 1 ], 10 );
continue;
}

// Process added lines (start with + but not +++)
if( line.startsWith( '+' ) && !line.startsWith( '+++' ) )
{
var addedContent = line.substring( 1 );

// Check if this line matches our TODO regex
regex.lastIndex = 0;
var match = regex.exec( addedContent );
if( match && currentFile )
{
var fsPath = path.join( workspaceFolder, currentFile );
var result = {
fsPath: fsPath,
uri: vscode.Uri.file( fsPath ),
line: currentLineNumber,
column: match.index + 1,
match: addedContent
};
debug( " Match (Git Branch): " + JSON.stringify( result ) );
searchResults.add( result );
}
currentLineNumber++;
}
else if( !line.startsWith( '-' ) && !line.startsWith( '\\' ) && line.length > 0 )
{
// Context line (no prefix) - shouldn't happen with --unified=0 but handle anyway
currentLineNumber++;
}
// Removed lines (start with -) don't affect line numbering for new file
}

resolve();
} );
} );
} ).catch( function( err )
{
vscode.window.showWarningMessage( "Todo-Tree: " + err.message );
return Promise.resolve();
} );
}

function addGlobs( source, target, exclude )
{
Object.keys( source ).map( function( glob )
Expand Down Expand Up @@ -470,7 +599,8 @@ function activate( context )

function refreshOpenFiles()
{
if( config.scanMode() !== SCAN_MODE_WORKSPACE_ONLY )
var scanMode = config.scanMode();
if( scanMode !== SCAN_MODE_WORKSPACE_ONLY && scanMode !== SCAN_MODE_GIT_BRANCH )
{
Object.keys( openDocuments ).map( function( document )
{
Expand Down Expand Up @@ -586,26 +716,48 @@ function activate( context )
statusBarIndicator.command = "todo-tree.stopScan";
statusBarIndicator.tooltip = "Click to interrupt scan";

searchList = getRootFolders();

if( searchList.length === 0 )
{
searchWorkspaces( searchList );
}
var scanMode = config.scanMode();

if( config.shouldIgnoreGitSubmodules() )
if( scanMode === SCAN_MODE_GIT_BRANCH )
{
submoduleExcludeGlobs = [];
searchList.forEach( function( rootPath )
// For git branch mode, search git diffs in each workspace folder
var workspaceFolders = vscode.workspace.workspaceFolders || [];
var gitSearchPromises = workspaceFolders.map( function( folder )
{
submoduleExcludeGlobs = submoduleExcludeGlobs.concat( utils.getSubmoduleExcludeGlobs( rootPath ) );
return searchGitBranch( folder.uri.fsPath );
} );
context.workspaceState.update( 'submoduleExcludeGlobs', submoduleExcludeGlobs );

Promise.all( gitSearchPromises )
.then( function()
{
debug( "Found " + searchResults.count() + " items in git branch diff" );
addResultsToTree();
setButtonsAndContext();
} );
}
else
{
searchList = getRootFolders();

if( searchList.length === 0 )
{
searchWorkspaces( searchList );
}

if( config.shouldIgnoreGitSubmodules() )
{
submoduleExcludeGlobs = [];
searchList.forEach( function( rootPath )
{
submoduleExcludeGlobs = submoduleExcludeGlobs.concat( utils.getSubmoduleExcludeGlobs( rootPath ) );
} );
context.workspaceState.update( 'submoduleExcludeGlobs', submoduleExcludeGlobs );
}

iterateSearchList()
.finally( refreshOpenFiles )
.then( addResultsToTree );
iterateSearchList()
.finally( refreshOpenFiles )
.then( addResultsToTree );
}
}

function triggerRescan()
Expand Down Expand Up @@ -970,6 +1122,11 @@ function activate( context )
vscode.workspace.getConfiguration( 'todo-tree.tree' ).update( 'scanMode', SCAN_MODE_WORKSPACE_ONLY, vscode.ConfigurationTarget.Workspace );
}

function scanGitBranch()
{
vscode.workspace.getConfiguration( 'todo-tree.tree' ).update( 'scanMode', SCAN_MODE_GIT_BRANCH, vscode.ConfigurationTarget.Workspace );
}

function dumpFolderFilter()
{
debug( "Folder filter include:" + JSON.stringify( context.workspaceState.get( 'includeGlobs' ) ) );
Expand Down Expand Up @@ -1254,7 +1411,8 @@ function activate( context )

function shouldRefreshFile()
{
return vscode.workspace.getConfiguration( 'todo-tree.tree' ).autoRefresh === true && config.scanMode() !== SCAN_MODE_WORKSPACE_ONLY;
var scanMode = config.scanMode();
return vscode.workspace.getConfiguration( 'todo-tree.tree' ).autoRefresh === true && scanMode !== SCAN_MODE_WORKSPACE_ONLY && scanMode !== SCAN_MODE_GIT_BRANCH;
}

// We can't do anything if we can't find ripgrep
Expand Down Expand Up @@ -1693,6 +1851,7 @@ function activate( context )
context.subscriptions.push( vscode.commands.registerCommand( 'todo-tree.scanOpenFilesOnly', scanOpenFilesOnly ) );
context.subscriptions.push( vscode.commands.registerCommand( 'todo-tree.scanCurrentFileOnly', scanCurrentFileOnly ) );
context.subscriptions.push( vscode.commands.registerCommand( 'todo-tree.scanWorkspaceOnly', scanWorkspaceOnly ) );
context.subscriptions.push( vscode.commands.registerCommand( 'todo-tree.scanGitBranch', scanGitBranch ) );

context.subscriptions.push( vscode.window.onDidChangeActiveTextEditor( function( e )
{
Expand Down Expand Up @@ -1764,11 +1923,12 @@ function activate( context )

delete openDocuments[ document.uri.toString() ];

if( vscode.workspace.getConfiguration( 'todo-tree.tree' ).autoRefresh === true && config.scanMode() !== SCAN_MODE_WORKSPACE_ONLY )
var scanMode = config.scanMode();
if( vscode.workspace.getConfiguration( 'todo-tree.tree' ).autoRefresh === true && scanMode !== SCAN_MODE_WORKSPACE_ONLY && scanMode !== SCAN_MODE_GIT_BRANCH )
{
if( config.isValidScheme( document.uri ) )
{
if( config.scanMode() !== SCAN_MODE_WORKSPACE_AND_OPEN_FILES )
if( scanMode !== SCAN_MODE_WORKSPACE_AND_OPEN_FILES )
{
removeFromTree( document.uri );
}
Expand Down