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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
out
node_modules
node_modules
.sync
40 changes: 36 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,38 @@
"configuration": "./todo.configuration.json"
}
],
"configuration": {
"type": "object",
"title": "ToDoTasks configuration",
"properties": {
"todotasks.showDateOnDone": {
"type": "boolean",
"default": true,
"description": "Appends the date to the end of a todo once marked as done"
},
"todotasks.useUTCDate": {
"type": "boolean",
"default": false,
"description": "Use UTC for the date"
},
"todotasks.newTaskSymbol": {
"type": "string",
"default": "☐",
"description": "Changes the symbol to denote a new task"
},
"todotasks.doneTaskSymbol": {
"type": "string",
"default": "✔",
"description": "Changes the symbol to denote a done task"
},
"todotasks.cancelTaskSymbol": {
"type": "string",
"default": "✘",
"description": "Changes the symbol to denote a canceled task"
}
}
},

"grammars": [
{
"language": "todo",
Expand Down Expand Up @@ -80,17 +112,17 @@
"command": "task.new",
"key": "Ctrl+Enter",
"mac": "Cmd+Enter",
"when": "editorTextFocus"
"when": "editorTextFocus && editorLangId == todo"
},
{
"command": "task.cancel",
"key": "Alt+c",
"when": "editorTextFocus"
"when": "editorTextFocus && editorLangId == todo"
},
{
"command": "task.complete",
"key": "Alt+d",
"when": "editorTextFocus"
"when": "editorTextFocus && editorLangId == todo"
}
]
},
Expand All @@ -104,4 +136,4 @@
"typescript": "^1.8.5",
"vscode": "^0.11.0"
}
}
}
22 changes: 22 additions & 0 deletions src/TodoConfiguration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict';

import { workspace} from 'vscode';

export class TodoConfiguration {
public static SYMBOL_NEW_TASK= "☐";
public static SYMBOL_DONE_TASK= "✔";
public static SYMBOL_CANCEL_TASK= "✘";

public static DATE_SHOW = true;
public static DATE_UTC = false;

public updateConfig()
{
TodoConfiguration.DATE_SHOW = <boolean> workspace.getConfiguration('todotasks').get('showDateOnDone');
TodoConfiguration.DATE_UTC = <boolean> workspace.getConfiguration('todotasks').get('useUTCDate');
TodoConfiguration.SYMBOL_NEW_TASK = <string> workspace.getConfiguration('todotasks').get('newTaskSymbol');
TodoConfiguration.SYMBOL_DONE_TASK = <string> workspace.getConfiguration('todotasks').get('doneTaskSymbol');
TodoConfiguration.SYMBOL_CANCEL_TASK = <string> workspace.getConfiguration('todotasks').get('cancelTaskSymbol');
}

}
34 changes: 19 additions & 15 deletions src/TodoDocument.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
'use strict';

import {TextDocument, TextLine, Position, CompletionItem, Range} from 'vscode';
import {TodoConfiguration} from './TodoConfiguration'
import {TextDocument, TextLine, Position, CompletionItem, Range, workspace} from 'vscode';

export class TodoDocument {

public static SYMBOL_PROJECT= ":";
public static SYMBOL_NEW_TASK= "☐";
public static SYMBOL_DONE_TASK= "✔";
public static SYMBOL_CANCEL_TASK= "✘";
public static SYMBOL_TAG= "@";

public static TAG_CRITICAL= "critical";
Expand All @@ -30,10 +28,16 @@ export class TodoDocument {
return null;
}

escapeRegExp(text) {
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}

public getTasks(): Task[] {
let result: Task[]= [];
var text= this._textDocument.getText();
var regEx= /^\s*[☐|✘|✔]/gm;
var regEx= new RegExp("^\\s*[" + this.escapeRegExp(TodoConfiguration.SYMBOL_NEW_TASK) + "|" +
this.escapeRegExp(TodoConfiguration.SYMBOL_CANCEL_TASK) + "|" + this.escapeRegExp(TodoConfiguration.SYMBOL_DONE_TASK) + "]", "gm");

var match;
while (match = regEx.exec(text)) {
let line= this._textDocument.lineAt(this._textDocument.positionAt(match.index + 1).line);
Expand All @@ -53,9 +57,9 @@ export class TodoDocument {

public isTask(pos: Position): boolean {
let task= this._textDocument.lineAt(pos.line).text.trim();
return task.startsWith(TodoDocument.SYMBOL_NEW_TASK)
|| task.startsWith(TodoDocument.SYMBOL_CANCEL_TASK)
|| task.startsWith(TodoDocument.SYMBOL_DONE_TASK);
return task.startsWith(TodoConfiguration.SYMBOL_NEW_TASK)
|| task.startsWith(TodoConfiguration.SYMBOL_CANCEL_TASK)
|| task.startsWith(TodoConfiguration.SYMBOL_DONE_TASK);
}

public static toTag(tagName: string): string {
Expand All @@ -74,27 +78,27 @@ export class Task {
public getDescription(): string {
if (this.isDone()) {
let index= this.taskText.indexOf(TodoDocument.toTag(TodoDocument.ACTION_DONE));
return index !== -1 ? this.taskText.substring(TodoDocument.SYMBOL_DONE_TASK.length, index).trim()
: this.taskText.substring(TodoDocument.SYMBOL_DONE_TASK.length).trim();
return index !== -1 ? this.taskText.substring(TodoConfiguration.SYMBOL_DONE_TASK.length, index).trim()
: this.taskText.substring(TodoConfiguration.SYMBOL_DONE_TASK.length).trim();
}
if (this.isCancelled()) {
var index= this.taskText.indexOf(TodoDocument.toTag(TodoDocument.ACTION_CANCELLED));
return index !== -1 ? this.taskText.substring(TodoDocument.SYMBOL_CANCEL_TASK.length, index).trim()
: this.taskText.substring(TodoDocument.SYMBOL_CANCEL_TASK.length).trim();
return index !== -1 ? this.taskText.substring(TodoConfiguration.SYMBOL_CANCEL_TASK.length, index).trim()
: this.taskText.substring(TodoConfiguration.SYMBOL_CANCEL_TASK.length).trim();
}
return this.taskText.substring(TodoDocument.SYMBOL_NEW_TASK.length).trim();
return this.taskText.substring(TodoConfiguration.SYMBOL_NEW_TASK.length).trim();
}

public isEmpty(): boolean {
return !this.getDescription().trim();
}

public isDone(): boolean {
return this.taskText.indexOf(TodoDocument.SYMBOL_DONE_TASK) !== -1;
return this.taskText.indexOf(TodoConfiguration.SYMBOL_DONE_TASK) !== -1;
}

public isCancelled(): boolean {
return this.taskText.indexOf(TodoDocument.SYMBOL_CANCEL_TASK) !== -1;
return this.taskText.indexOf(TodoConfiguration.SYMBOL_CANCEL_TASK) !== -1;
}

public hasTag(tag: string): boolean {
Expand Down
49 changes: 43 additions & 6 deletions src/TodoDocumentDecorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { window, TextEditor, Range, Position, TextLine, TextDocumentChangeEvent, TextEditorDecorationType } from 'vscode';
import {TodoDocument, Task} from './TodoDocument';
import {TodoConfiguration} from './TodoConfiguration';

export default class TodoDocumentEditor {

Expand Down Expand Up @@ -92,7 +93,7 @@ class DoneTasksDecorator extends LineDecorator {
var tagsRanges:Range[]= []
tasks.forEach((task:Task) => {
if (task.isDone()) {
doneSymbolRanges.push(this.getDoneSymbolRange(task));
doneSymbolRanges= doneSymbolRanges.concat(this.getDoneSymbolRange(task));
tagsRanges= tagsRanges.concat(task.getTagsRanges());
doneActionRanges.push(this.getDoneActionRange(task));
}
Expand All @@ -102,8 +103,22 @@ class DoneTasksDecorator extends LineDecorator {
{decorationType: DoneTasksDecorator.DECORATOR_DONE_ACTION, ranges: doneActionRanges}];
}

private getDoneSymbolRange(doneTask: Task): Range {
return super.getRange(TodoDocument.SYMBOL_DONE_TASK, doneTask.taskLine);
private getDoneSymbolRange(doneTask: Task): Range[] {

var rangeMinusTags:Range[]= [];
var range:Range = super.getRange(doneTask.taskLine.text, doneTask.taskLine);

doneTask.getTagsRanges().concat(this.getDoneActionRange(doneTask)).forEach((tagRange:Range) => {
if(range.contains(tagRange))
{
rangeMinusTags.push(new Range(range.start, tagRange.start));
range = new Range(tagRange.end, range.end);
}
});

rangeMinusTags.push(range);

return rangeMinusTags;
}

private getDoneActionRange(doneTask: Task): Range {
Expand Down Expand Up @@ -147,7 +162,7 @@ class CancelTasksDecorator extends LineDecorator {

tasks.forEach((task:Task) => {
if (task.isCancelled()) {
doneSymbolRanges.push(this.getCancelSymbolRange(task));
doneSymbolRanges = doneSymbolRanges.concat(this.getCancelSymbolRange(task));
tagsRanges= tagsRanges.concat(task.getTagsRanges());
doneActionRanges.push(this.getCancelActionRange(task));
}
Expand All @@ -158,8 +173,21 @@ class CancelTasksDecorator extends LineDecorator {
{decorationType: CancelTasksDecorator.DECORATOR_CANCEL_ACTION, ranges: doneActionRanges}];
}

private getCancelSymbolRange(doneTask: Task): Range {
return super.getRange(TodoDocument.SYMBOL_CANCEL_TASK, doneTask.taskLine);
private getCancelSymbolRange(doneTask: Task): Range[] {
var rangeMinusTags:Range[]= [];
var range:Range = super.getRange(doneTask.taskLine.text, doneTask.taskLine);

doneTask.getTagsRanges().concat(this.getCancelActionRange(doneTask)).forEach((tagRange:Range) => {
if(range.contains(tagRange))
{
rangeMinusTags.push(new Range(range.start, tagRange.start));
range = new Range(tagRange.end, range.end);
}
});

rangeMinusTags.push(range);

return rangeMinusTags;
}

private getCancelActionRange(doneTask: Task): Range {
Expand Down Expand Up @@ -189,6 +217,14 @@ class TagsDecorator extends LineDecorator {
color: '#000',
}
});
private static DECORATOR_TAG= window.createTextEditorDecorationType({
light: {
color: '#ccc',
},
dark: {
color: '#7D7D7D',
}
});

private static DECORATOR_TODAY_TAG= window.createTextEditorDecorationType({
light: {
Expand All @@ -206,6 +242,7 @@ class TagsDecorator extends LineDecorator {
var highTagRanges:Range[]= []
var lowTagRanges:Range[]= []
var todayTagRanges:Range[]= []

tasks.forEach((task:Task) => {
if (!task.isCancelled() && !task.isDone()) {
criticalTagRanges= criticalTagRanges.concat(task.getTagRanges(TodoDocument.TAG_CRITICAL));
Expand Down
24 changes: 17 additions & 7 deletions src/TodoDocumentEditor.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
'use strict';

import { commands, TextEditor, TextEditorEdit, Range, Position, TextLine, TextDocumentChangeEvent } from 'vscode';
import { commands, TextEditor, TextEditorEdit, Range, Position, TextLine, TextDocumentChangeEvent, workspace } from 'vscode';
import {TodoDocument} from './TodoDocument';
import TodoDocumentDecorator from './TodoDocumentDecorator';
import {TodoConfiguration} from './TodoConfiguration';

export class TodoDocumentEditor {
constructor(private _textEditor: TextEditor, private _textEditorEdit: TextEditorEdit) {
}
Expand All @@ -18,7 +20,7 @@ export class TodoDocumentEditor {

let taskLine= this._textEditor.document.lineAt(this._textEditor.selection.active);
let taskDescription= taskLine.text.trim();
this.updateTask(taskLine, taskDescription, TodoDocument.SYMBOL_NEW_TASK);
this.updateTask(taskLine, taskDescription, TodoConfiguration.SYMBOL_NEW_TASK);
}

public completeCurrentTask() {
Expand All @@ -31,11 +33,11 @@ export class TodoDocumentEditor {
}

if (task.isDone()) {
this.updateTask(task.taskLine, task.getDescription(), TodoDocument.SYMBOL_NEW_TASK);
this.updateTask(task.taskLine, task.getDescription(), TodoConfiguration.SYMBOL_NEW_TASK);
return;
}

this.updateTask(task.taskLine, task.getDescription(), TodoDocument.SYMBOL_DONE_TASK, TodoDocument.ACTION_DONE);
this.updateTask(task.taskLine, task.getDescription(), TodoConfiguration.SYMBOL_DONE_TASK, TodoDocument.ACTION_DONE);
}

public cancelCurrentTask() {
Expand All @@ -53,16 +55,24 @@ export class TodoDocumentEditor {
return;
}
if (task.isCancelled()) {
this.updateTask(task.taskLine, task.getDescription(), TodoConfiguration.SYMBOL_NEW_TASK);
return;
}

this.updateTask(task.taskLine, task.getDescription(), TodoDocument.SYMBOL_CANCEL_TASK, TodoDocument.ACTION_CANCELLED);
this.updateTask(task.taskLine, task.getDescription(), TodoConfiguration.SYMBOL_CANCEL_TASK, TodoDocument.ACTION_CANCELLED);
}

private updateTask(taskLine: TextLine, taskDescription: string, symbol: string, tag?: string) {
var timestamp = new Date();
this._textEditorEdit.delete(new Range(new Position(taskLine.lineNumber, taskLine.firstNonWhitespaceCharacterIndex), taskLine.range.end));
this.insertTask(new Position(taskLine.lineNumber, taskLine.firstNonWhitespaceCharacterIndex), symbol + " " + taskDescription + (tag ? (" " + TodoDocument.toTag(tag)+' (' + timestamp.toLocaleString() + ')'): ""));

var timestamp = new Date();
var dateOptions = TodoConfiguration.DATE_UTC ? { timeZone: "UTC", timeZoneName: "short" } : {};

var showDate = TodoConfiguration.DATE_SHOW;
var tagText = " " + TodoDocument.toTag(tag)+ (showDate ? (' (' + timestamp.toLocaleString(undefined, dateOptions) + ')'): "" );
var newLine = symbol + " " + taskDescription + (tag ? (tagText): "");

this.insertTask(new Position(taskLine.lineNumber, taskLine.firstNonWhitespaceCharacterIndex), newLine);
}

private insertTask(pos: Position, task: string) {
Expand Down
5 changes: 5 additions & 0 deletions src/TodoExtensionMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { workspace, window, ExtensionContext, TextDocumentChangeEvent, languages, env, commands, TextEditor, TextEditorEdit, Position } from 'vscode';
import {TodoCommands} from './TodoCommands';
import {TodoConfiguration} from './TodoConfiguration';
import TodoCompletionItemProvider from './TodoCompletionItemProvider';
import TodoDocumentDecorator from './TodoDocumentDecorator';
import TodoCodeActionProvider from './TodoCodeActionProvider';
Expand All @@ -19,6 +20,10 @@ export function activate(context: ExtensionContext): any {
}
});

let todoConfiguration = new TodoConfiguration();
todoConfiguration.updateConfig();
workspace.onDidChangeConfiguration(() => todoConfiguration.updateConfig());

let todoCommands= new TodoCommands();
context.subscriptions.push(todoCommands.registerNewTaskCommand());
context.subscriptions.push(todoCommands.registerCompleteTaskCommand());
Expand Down