Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Settings for customizing bullet points and appending the date on done #33

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
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