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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
- Fixed time tags on comments or project headers being counted as estimates for the preceding pending todo.
- Fixed Todo and embedded file grouping so similarly prefixed sibling folders are not treated as workspace roots.
- Preserved CRLF line endings when merging tasks into existing archive files.
- Added project statistics to the Archive header so estimates can be compared with elapsed time across archived tasks. Fixes: https://github.com/vycdev/vscode-todo-plus-two/issues/87

### Version 5.2.0

Expand Down
17 changes: 17 additions & 0 deletions src/utils/statistics-lines.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@ interface StatisticsItems<T extends StatisticsLine> {
tags?: T[];
}

export const getStatisticsScopeEnd = <T>(
lines: T[],
startIndex: number,
scopeLevel: number,
getLevel: (line: T) => number,
isTag: (line: T) => boolean,
includeRemainingDocument = false
): number => {
if (includeRemainingDocument) return lines.length;

for (let index = startIndex + 1; index < lines.length; index++) {
if (!isTag(lines[index]) && getLevel(lines[index]) <= scopeLevel) return index;
}

return lines.length;
};

const mergeSorted = <T extends StatisticsLine>(left: T[], right: T[]): T[] => {
const merged = new Array<T>(left.length + right.length);

Expand Down
38 changes: 29 additions & 9 deletions src/utils/statistics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Consts from '../consts';
import { Comment, Project, Tag, TodoBox, TodoDone, TodoCancelled } from '../todo/items';
import AST from './ast';
import { getEstimateDuration } from './estimate';
import { getStatisticsLines } from './statistics-lines';
import { getStatisticsLines, getStatisticsScopeEnd } from './statistics-lines';
import Tokens from './statistics_tokens';
import Time from './time';

Expand Down Expand Up @@ -234,29 +234,50 @@ const Statistics = {

if (!items.projects) return;

const lines = getStatisticsLines(items);
const lines = getStatisticsLines(items),
archiveLineNumber = items.archive && items.archive.lineNumber;

items.projects.forEach((project) => {
Statistics.tokens.updateProject(
textDocument,
project,
lines,
lines.indexOf(project)
lines.indexOf(project),
archiveLineNumber
);
});
},

updateProject(textDocument: vscode.TextDocument, project, lines, lineNr: number) {
updateProject(
textDocument: vscode.TextDocument,
project,
lines,
lineNr: number,
archiveLineNumber?: number
) {
if (Statistics.tokens.projects[project.lineNumber])
return Statistics.tokens.projects[project.lineNumber];

project.level = project.level || AST.getLevel(textDocument, project.line.text);

const tokens = new Tokens();
const tokens = new Tokens(),
includeRemainingDocument = project.lineNumber === archiveLineNumber,
scopeEnd = getStatisticsScopeEnd(
lines,
lineNr,
project.level,
(item: any) => {
item.level = item.level || AST.getLevel(textDocument, item.line.text);

return item.level;
},
(item) => item instanceof Tag,
includeRemainingDocument
);

let wasPending = false;

for (let i = lineNr + 1, l = lines.length; i < l; i++) {
for (let i = lineNr + 1; i < scopeEnd; i++) {
const nextItem = lines[i];

if (nextItem instanceof Tag) {
Expand All @@ -272,16 +293,15 @@ const Statistics = {
nextItem.level =
nextItem.level || AST.getLevel(textDocument, nextItem.line.text);

if (nextItem.level <= project.level) break;

wasPending = nextItem instanceof TodoBox;

if (nextItem instanceof Project) {
const nextTokens = Statistics.tokens.updateProject(
textDocument,
nextItem,
lines,
i
i,
archiveLineNumber
);

tokens.comments += nextTokens.comments;
Expand Down
48 changes: 47 additions & 1 deletion test/statistics-lines.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect } from 'chai';
import { getStatisticsLines } from '../src/utils/statistics-lines';
import { getStatisticsLines, getStatisticsScopeEnd } from '../src/utils/statistics-lines';

describe('Statistics line ordering', () => {
it('places each line context before tags on the same line', () => {
Expand Down Expand Up @@ -47,4 +47,50 @@ describe('Statistics line ordering', () => {

expect(tagStates).to.deep.equal([false]);
});

it('stops normal project statistics at a peer project', () => {
const lines = [
{ type: 'project', level: 0 },
{ type: 'todo', level: 1 },
{ type: 'tag', level: 1 },
{ type: 'project', level: 0 },
{ type: 'todo', level: 1 },
],
end = getStatisticsScopeEnd(
lines,
0,
0,
(line) => line.level,
(line) => line.type === 'tag'
);

expect(lines.slice(1, end).map((line) => line.type)).to.deep.equal(['todo', 'tag']);
});

it('includes peer and nested projects through the end of an archive', () => {
const lines = [
{ type: 'archive', level: 0 },
{ type: 'todo', level: 0 },
{ type: 'project', level: 0 },
{ type: 'todo', level: 1 },
{ type: 'project', level: 1 },
{ type: 'todo', level: 2 },
],
end = getStatisticsScopeEnd(
lines,
0,
0,
(line) => line.level,
(line) => line.type === 'tag',
true
);

expect(lines.slice(1, end).map((line) => line.type)).to.deep.equal([
'todo',
'project',
'todo',
'project',
'todo',
]);
});
});