Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ artifacts/vsix/
artifacts/release-notes/
artifacts/**/*.log
artifacts/perf/runtime-benchmarks.json
artifacts/perf/*.json
artifacts/**/*.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Runtime Benchmarks

- Baseline ref: `a6f60e0ce830c4649ac34fc05e5a1799ec91d151`
- Current source: working tree
- Node: `v25.2.0`
- Selection mode: `scenario-list`
- Declared suite: `user-flow`
- Result-count validation: `1 rows, suite-consistent=true, all-user-flow=true`

## Machine Profile

| Category | Field | Value |
| --- | --- | --- |
| Host | Hostname | n00ne-AERO-17-YD |
| Host | OS | Ubuntu 22.04.5 LTS |
| Host | Kernel | 6.8.0-124-generic |
| Host | Architecture | x64 |
| Host | Load Average | 2.5, 3.34, 3.68 |
| Host | Available Parallelism | - |
| CPU | Model | Intel(R) Core(TM) i9-14900HX |
| CPU | Vendor | GenuineIntel |
| CPU | Topology | 16 logical CPU(s), 2 thread(s)/core, 8 core(s)/socket, 1 socket(s), 1 NUMA node(s) |
| CPU | Frequency | 800 MHz to 5,800 MHz |
| CPU | Cache | L1d 384 KiB (8 instances), L1i 256 KiB (8 instances), L2 16 MiB (8 instances), L3 36 MiB (1 instance) |
| Memory | Total RAM | 62.51 GiB (`67,119,767,552 bytes`) |
| Memory | Available At Collection | 14.21 GiB (`15,261,028,352 bytes`) |
| Memory | Online Physical RAM | 66.00 GiB (`70,866,960,384 bytes`) |
| Memory | Swap | total 120 GiB (`128,848,973,824 bytes`); free 105 GiB (`112,411,762,688 bytes`) |
| Memory | DMI / SPD | Unavailable: /sys/firmware/dmi/tables/smbios_entry_point: Permission denied /dev/mem: Permission denied |
| Storage | Root Device | nvme1n1 (Samsung SSD 9100 PRO 4TB), 3.64 TiB (`4,000,787,030,016 bytes`), transport nvme, rotational=false, readOnly=false |

## Scenario Model

| Scenario | Kind | User flow | Measurement scope | Input model |
| --- | --- | --- | --- | --- |
| open-file-default-save-rescan-visible-tree | user-flow | Save an already-open file that uses default tag scanning and redraw the visible tree. | Document save listener, document rescan, search-result replacement, and visible-tree render. | Real document text in a VS Code event harness. |

## Metric Model

| Table | Value model | Accuracy model |
| --- | --- | --- |
| Latency | Wall-clock elapsed time around each harness flow iteration, summarized as min/p50/p90/p95/max. | Exact for each sampled iteration in this run. |
| Profiled RSS Burst | Difference between the isolated scenario worker RSS measured immediately before the flow and that worker iteration's OS high-water-mark peak RSS. | Exact for the measured worker iteration, using `process.memoryUsage().rss` at flow start and `process.resourceUsage().maxRSS` for the peak. |
| Profiled Peak RSS | Highest process RSS reached by each isolated scenario worker iteration. | Exact worker-process high-water mark from `process.resourceUsage().maxRSS`. |

## Latency

| Scenario | Kind | Baseline p50 ms | Current p50 ms | Baseline p90 ms | Current p90 ms | Baseline p95 ms | Current p95 ms |
| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: |
| open-file-default-save-rescan-visible-tree | user-flow | 104.44 | 2.15 | 110.7 | 2.46 | 111.64 | 3.76 |

## Profiled RSS Burst

| Scenario | Kind | Baseline p50 MiB | Current p50 MiB | Baseline p90 MiB | Current p90 MiB | Baseline p95 MiB | Current p95 MiB | Baseline Max MiB | Current Max MiB |
| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| open-file-default-save-rescan-visible-tree | user-flow | 26.88 | 24.13 | 27.25 | 27.25 | 27.25 | 27.63 | 27.25 | 27.63 |

## Profiled Peak RSS

| Scenario | Kind | Baseline p50 RSS MiB | Current p50 RSS MiB | Baseline p90 RSS MiB | Current p90 RSS MiB | Baseline p95 RSS MiB | Current p95 RSS MiB | Baseline Max RSS MiB | Current Max RSS MiB |
| --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| open-file-default-save-rescan-visible-tree | user-flow | 82.47 | 79.63 | 82.62 | 82.76 | 82.68 | 83.16 | 82.68 | 83.16 |
43 changes: 24 additions & 19 deletions src/extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,6 @@ function activate( context )
return undefined;
}

changedEntries.forEach( function( entry )
{
extensionContextValues[ entry.suffix ] = entry.value;
} );

return Promise.all( changedEntries.map( function( entry )
{
var updates = [
Expand All @@ -210,7 +205,13 @@ function activate( context )
}

return Promise.all( updates );
} ) );
} ) ).then( function()
{
changedEntries.forEach( function( entry )
{
extensionContextValues[ entry.suffix ] = entry.value;
} );
} );
} );

extensionContextUpdateQueue = scheduled.catch( function( error )
Expand Down Expand Up @@ -901,22 +902,26 @@ function activate( context )
}, 200 );
}

function tagCountTotal( counts )
{
return Object.values( counts ).reduce( function( total, count ) { return total + count; }, 0 );
}

function updateInformation()
{
var statusBar = getSetting( 'general.statusBar', 'none' );

var counts = provider.getTagCountsForActivityBar();
var total = Object.values( counts ).reduce( function( a, b ) { return a + b; }, 0 );
var activityBarCounts = provider.getTagCountsForActivityBar();
var activityBarTotal = tagCountTotal( activityBarCounts );
var counts = statusBar === STATUS_BAR_CURRENT_FILE ?
provider.getTagCountsForStatusBar( getCurrentFileFilter() ) :
provider.getTagCountsForStatusBar();
var total = tagCountTotal( counts );
var titleTotal = statusBar === STATUS_BAR_CURRENT_FILE ? total : activityBarTotal;

var badgeTotal = config.shouldShowActivityBarBadge() ? total : 0;
var badgeTotal = config.shouldShowActivityBarBadge() ? activityBarTotal : 0;
todoTreeView.badge = { value: badgeTotal };

if( statusBar === STATUS_BAR_CURRENT_FILE )
{
counts = provider.getTagCountsForStatusBar( getCurrentFileFilter() );
total = Object.values( counts ).reduce( function( a, b ) { return a + b; }, 0 );
}

var countRegex = new RegExp( "([^(]*)(\\(\\d+\\))*" );
var match = countRegex.exec( todoTreeView.title );
if( match !== null )
Expand All @@ -935,9 +940,9 @@ function activate( context )
title = "Tree";
}

if( total > 0 && getSetting( 'tree.showCountsInTree', false ) === true )
if( titleTotal > 0 && getSetting( 'tree.showCountsInTree', false ) === true )
{
title += " (" + total + ")";
title += " (" + titleTotal + ")";
}
todoTreeView.title = title;
}
Expand Down Expand Up @@ -2891,7 +2896,7 @@ function activate( context )
var buttons = [ MORE_INFO_BUTTON, NEVER_SHOW_AGAIN_BUTTON ];
if( getSetting( 'regex.regex', '' ) === getCurrentConfiguration().inspect( 'regex.regex' ).defaultValue )
{
message += " Would you like to update your settings automatically?";
message += " Update settings automatically?";
buttons.unshift( YES_BUTTON );
}
vscode.window.showInformationMessage( message, ...buttons ).then( function( button )
Expand Down Expand Up @@ -3057,7 +3062,7 @@ function activate( context )
destinationId: 'workbench.view.explorer'
} );

vscode.window.showInformationMessage( identity.DISPLAY_NAME + ": 'showInExplorer' has been deprecated. If needed, the view can now be dragged to where you want it.", OPEN_SETTINGS_BUTTON, NEVER_SHOW_AGAIN_BUTTON ).then( function( button )
vscode.window.showInformationMessage( identity.DISPLAY_NAME + ": 'showInExplorer' has been deprecated. View can be dragged to any VS Code location.", OPEN_SETTINGS_BUTTON, NEVER_SHOW_AGAIN_BUTTON ).then( function( button )
{
if( button === OPEN_SETTINGS_BUTTON )
{
Expand Down
132 changes: 121 additions & 11 deletions test/extension.scan-parity.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,10 @@ function createSearchResultsStub()
};
}

function createProviderStub()
function createProviderStub( options )
{
options = options || {};

return {
replaceCalls: [],
latestResultsByUri: new Map(),
Expand All @@ -276,8 +278,15 @@ function createProviderStub()
},
finalizePendingChanges: function( filter, options ) { this.finalizeCalls.push( { filter: filter, options: options } ); },
refresh: function() { this.refreshCalls++; },
getTagCountsForActivityBar: function() { return {}; },
getTagCountsForStatusBar: function() { return {}; },
getTagCountsForActivityBar: function()
{
return Object.assign( {}, options.activityBarCounts || {} );
},
getTagCountsForStatusBar: function( fileFilter )
{
var byFile = options.statusBarCountsByFile || {};
return Object.assign( {}, fileFilter && byFile[ fileFilter ] ? byFile[ fileFilter ] : options.statusBarCounts || {} );
},
exportTree: function() { return {}; },
hasSubTags: function() { return false; },
getChildren: function() { return []; },
Expand Down Expand Up @@ -386,7 +395,7 @@ function createVscodeStub( options )
groupedBySubTag: false,
hideTreeWhenEmpty: false,
autoRefresh: true,
scanAtStartup: true,
scanAtStartup: options.scanAtStartup !== false,
scanMode: options.scanMode
},
filtering: filteringDefaults,
Expand All @@ -396,7 +405,7 @@ function createVscodeStub( options )
periodicRefreshInterval: periodicRefreshInterval,
rootFolder: "",
tags: languageMatrix.DEFAULT_TAGS.slice(),
statusBar: 'total'
statusBar: options.statusBar || 'total'
},
ripgrep: {
ripgrepArgs: '',
Expand All @@ -411,9 +420,9 @@ function createVscodeStub( options )
periodicRefreshInterval: periodicRefreshInterval,
rootFolder: "",
exportPath: '/tmp/todo-tree.txt',
statusBar: 'total',
statusBar: options.statusBar || 'total',
statusBarClickBehaviour: '',
showActivityBarBadge: false,
showActivityBarBadge: options.showActivityBarBadge === true,
tags: languageMatrix.DEFAULT_TAGS.slice(),
tagGroups: {},
schemes: [ 'file' ]
Expand All @@ -425,7 +434,7 @@ function createVscodeStub( options )
showBadges: false,
scanMode: options.scanMode,
showCurrentScanMode: false,
scanAtStartup: true,
scanAtStartup: options.scanAtStartup !== false,
hideTreeWhenEmpty: false,
buttons: rootSection.get( 'tree.buttons' )
}, undefined, configurationUpdates );
Expand Down Expand Up @@ -615,7 +624,15 @@ function createVscodeStub( options )
commands: {
executeCommand: function( command )
{
executedCommands.push( Array.prototype.slice.call( arguments ) );
var commandArguments = Array.prototype.slice.call( arguments );
executedCommands.push( commandArguments );
if( typeof options.executeCommandImpl === 'function' )
{
return Promise.resolve().then( function()
{
return options.executeCommandImpl.apply( undefined, commandArguments );
} );
}
return Promise.resolve();
},
registerCommand: function( name, handler )
Expand Down Expand Up @@ -703,7 +720,7 @@ function createVscodeStub( options )

function createExtensionHarness( options )
{
var provider = options.useActualTreeProvider === true ? undefined : createProviderStub();
var provider = options.useActualTreeProvider === true ? undefined : createProviderStub( options );
var searchResults = createSearchResultsStub();
var ripgrepSearchCalls = [];
var scanDocumentCalls = [];
Expand Down Expand Up @@ -833,7 +850,7 @@ function createExtensionHarness( options )
shouldIgnoreGitSubmodules: function() { return false; },
shouldUseBuiltInFileExcludes: function() { return false; },
shouldUseBuiltInSearchExcludes: function() { return false; },
shouldShowActivityBarBadge: function() { return false; },
shouldShowActivityBarBadge: function() { return options.showActivityBarBadge === true; },
shouldFlatten: function() { return Object.prototype.hasOwnProperty.call( treeStateOverrides, 'flat' ) ? treeStateOverrides.flat : context.workspaceState.get( 'flat', false ); },
shouldShowTagsOnly: function() { return Object.prototype.hasOwnProperty.call( treeStateOverrides, 'tagsOnly' ) ? treeStateOverrides.tagsOnly : context.workspaceState.get( 'tagsOnly', false ); },
clickingStatusBarShouldRevealTree: function() { return false; },
Expand Down Expand Up @@ -1359,6 +1376,99 @@ function fireVisibleNotebookEditorsChanged( harness, notebooksToShow, activeNote

QUnit.module( "extension scan parity" );

QUnit.test( "issue #41 total status bar uses status counts and activity badge uses activity counts", function( assert )
{
var harness = createExtensionHarness( {
scanMode: 'open files',
statusBar: 'total',
showActivityBarBadge: true,
statusBarCounts: { TODO: 2 },
activityBarCounts: { TODO: 2, FIXME: 5 },
fileContents: {}
} );

harness.extension.activate( harness.context );

return matrixHelpers.flushAsyncWork().then( function()
{
assert.equal( harness.vscode.statusBarItems[ 0 ].text, '$(check) 2 (in open files)' );
assert.deepEqual( harness.vscode.treeViews[ 0 ].badge, { value: 7 } );
} );
} );

QUnit.test( "issue #41 tag status bar ignores activity-bar hidden-count domain", function( assert )
{
var harness = createExtensionHarness( {
scanMode: 'open files',
statusBar: 'tags',
statusBarCounts: { TODO: 3, FIXME: 1 },
activityBarCounts: { TODO: 3 },
fileContents: {}
} );

harness.extension.activate( harness.context );

return matrixHelpers.flushAsyncWork().then( function()
{
assert.equal( harness.vscode.statusBarItems[ 0 ].text, '$(check) FIXME: 1 TODO: 3 (in open files)' );
} );
} );

QUnit.test( "command context cache updates only after VS Code accepts every setContext command", function( assert )
{
var flatFailures = 1;
var harness = createExtensionHarness( {
scanMode: 'open files',
scanAtStartup: false,
fileContents: {},
executeCommandImpl: function( command, contextKey )
{
if( command === 'setContext' && contextKey === 'better-todo-tree-flat' && flatFailures > 0 )
{
flatFailures--;
return Promise.reject( new Error( 'setContext test failure' ) );
}

return Promise.resolve();
}
} );

harness.extension.activate( harness.context );

return matrixHelpers.flushAsyncWork().then( function()
{
var beforeRetry = harness.vscode.executedCommands.length;

assert.equal( flatFailures, 0 );
assert.ok( harness.vscode.errorMessages.some( function( message )
{
return message.indexOf( 'failed to update command contexts' ) >= 0;
} ) );

harness.vscode.workspaceListeners.configuration( {
affectsConfiguration: function( section )
{
return section === 'better-todo-tree' || section === 'todo-tree';
}
} );

return matrixHelpers.flushAsyncWork().then( function()
{
return matrixHelpers.flushAsyncWork();
} ).then( function()
{
var retryCommands = harness.vscode.executedCommands.slice( beforeRetry );

assert.ok( retryCommands.some( function( commandArguments )
{
return commandArguments[ 0 ] === 'setContext' &&
commandArguments[ 1 ] === 'better-todo-tree-flat' &&
commandArguments[ 2 ] === false;
} ) );
} );
} );
} );

QUnit.test( "open-files mode stores canonical document results through the refresh pipeline", function( assert )
{
var fixture = [ {
Expand Down
Loading