ActionLock
code:package.json
{
"name": "actionlock",
"displayName": "ActionLock",
"version": "1.2.0",
"author": {
"name": "Nobuhito SATO",
"email": "nobuhito.sato@gmail.com",
},
"publisher": "nobuhito",
"engines": {
"vscode": "^1.18.0"
},
"categories": [
"Other"
],
"repository": {
},
"activationEvents": [
"onLanguage:markdown",
"onCommand:extension.doAction",
"onCommand:extension.toggleTask"
],
"license": "SEE LICENSE IN LICENSE",
"icon": "icon.png",
"main": "./src/extension",
"contributes": {
"configuration": {
"type": "object",
"title": "ActionLock configuration",
"properties": {
"actionlock.underlineColor": {
"type": "string",
"default": "#5b7e91",
"description": "Underline color of the character that ActionLock fires (default color is 舛花色)"
},
"actionlock.switchWords": {
"type": "array",
"default": [
[
"🚀 Rocket",
"😺 Cat",
"🐶 Dog"
],
[
"true",
"false"
]
],
"description": "ActionLock switch words"
}
}
},
"keybindings": [
{
"command": "extension.doAction",
"key": "Enter",
"when": "actionlock.isTrue"
}
],
"commands": [
{
"command": "extension.doAction",
"title": "Execute ActionLock"
},
{
"command": "extension.toggleTask",
"title": "Toggle task for mdtasks",
"when": "actionlock.isInstalledMDTasks"
}
]
},
"scripts": {
"postinstall": "node ./node_modules/vscode/bin/install",
"test": "node ./node_modules/vscode/bin/test"
},
"devDependencies": {
"@types/mocha": "^2.2.42",
"@types/node": "^7.0.0",
"eslint": "^5.12.0",
"mocha": "^5.2.0",
"typescript": "^2.5.2",
"vscode": "^1.1.26"
},
"dependencies": {
"moment": "^2.23.0"
}
}
code:src/extension.js
const vscode = require('vscode');
const ActionLock = require("./actionlock")
const moment = require("moment");
var decorationTypes = [];
function activate(context) {
let isInstalledMdtasks = (vscode.extensions.all.filter((d) => { return d.id == "nobuhito.mdtasks"; }).length > 0);
let ac = new ActionLock(isInstalledMdtasks);
context.subscriptions.push(vscode.commands.registerTextEditorCommand("extension.doAction", editor => {
doAction(ac, editor);
}));
vscode.commands.executeCommand("setContext", "actionlock.isInstalledMDTasks", false);
if (isInstalledMdtasks) {
vscode.commands.executeCommand("setContext", "actionlock.isInstalledMDTasks", true);
context.subscriptions.push(vscode.commands.registerTextEditorCommand("extension.toggleTask", editor => {
toggleTask(ac, editor);
}));
}
var select = vscode.window.onDidChangeTextEditorSelection((event) => {
triggerUpdate(event.textEditor);
});
context.subscriptions.push(select);
var timeout = null;
function triggerUpdate(editor) {
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => {
updateDecorations(ac, editor);
vscode.commands.executeCommand("setContext", "actionlock.isTrue", false);
let range = ac.getRangeAtCursor(editor);
if (range != null) {
vscode.commands.executeCommand("setContext", "actionlock.isTrue", true);
}
}, 100);
}
let editor = vscode.window.activeTextEditor;
if (editor) {
triggerUpdate(editor);
}
}
exports.activate = activate;
// this method is called when your extension is deactivated
function deactivate() {
}
exports.deactivate = deactivate;
function toggleTask(ac, editor) {
let line = editor.selection.start.line;
let position = ac.findCheckboxAtCursorLine(line);
editor.selection = new vscode.Selection(position, position);
doAction(ac, editor);
}
function updateDecorations(ActionLock, editor) {
for (const decorationType of decorationTypes) {
decorationType.dispose();
}
decorationTypes = [];
decorationTypes.push(ActionLock.updateDecorations(editor));
}
function doAction(ac, editor) {
let range = ac.getRangeAtCursor(editor);
editor.selection = new vscode.Selection(range.start, range.end);
let word = editor.document.getText(range);
let isDate = ac.regexDate.test(word);
if (isDate) {
showQuickPick(ac);
} else {
let edits = [];
let switchWords = ac.switchWords;
for (let items of switchWords) {
let index = items.indexOf(word);
if (index > -1) {
let dist = (items.length == index + 1) ? items0 : itemsindex + 1; edits.push({ range: range, dist: dist });
let isMdtasksItem = (["x", " "].indexOf(word) > -1) ? true : false; if (ac.isInstalledMdtasks && isMdtasksItem) {
edits = ac.checkParentTasks(edits, editor.document.getText().split(/\r?\n/));
}
var selection = new vscode.Position(range.start.line, range.start.character + dist.length);
editor.selection = new vscode.Selection(selection, selection);
break;
}
}
editor.edit(edit => {
for (const e of edits) {
edit.replace(e.range, e.dist);
}
}).then(() => {
if (ac.isInstalledMdtasks) {
let lineAt = editor.document.lineAt(range.start.line);
let doneDateReg = /\s\->\s\d{4}\-\d{2}\-\d{2}/;
let newText = (doneDateReg.test(lineAt.text)) ?
lineAt.text.replace(doneDateReg, "") :
lineAt.text + " -> " + moment().format("YYYY-MM-DD");
editor.edit(edit => {
edit.replace(lineAt.range, newText);
});
}
});
}
}
function showQuickPick(ac) {
let items = ac.buildQuickPick();
let editor = vscode.window.activeTextEditor;
var range = new vscode.Range(editor.selection.start, editor.selection.end);
let options = { matchOnDescription: true, placeHolder: "Select date or Close with escape key" };
vscode.window.showQuickPick(items, options).then((select) => {
if (select != undefined) {
editor.edit((edit) => {
edit.replace(range, select.label);
});
}
});
}
code:src/actionlock.js
const vscode = require('vscode');
const moment = require("moment");
module.exports = class ActionLock {
constructor(isInstalledMdtasks) {
this.isInstalledMdtasks = isInstalledMdtasks;
this.decorationType = null;
let myConf = vscode.workspace.getConfiguration("actionlock");
this.switchWords = myConf.get("switchWords");
if (isInstalledMdtasks) {
this.switchWords.push(["x", " "]); }
this.switchArray = [];
for (const items of this.switchWords) {
for (const item of items) {
this.switchArray.push(item);
}
}
this.regexWord = new RegExp(this.switchArray.map((d) => {
return this.regexpEscape(d);
}).join("|"), "g");
this.underlineColor = myConf.get("underlineColor");
this.ranges = [];
}
makeRanges(lines) {
let match;
this.ranges = [];
for (let i = 0; i < lines.length; i++) {
// 日付
while ((match = this.regexDate.exec(line)) !== null) {
let range = new vscode.Range(i, match.index, i, match.index + match0.length); this.ranges.push(range);
}
// ユーザー定義
while ((match = this.regexWord.exec(line)) != null) {
let range = new vscode.Range(i, match.index, i, match.index + match0.length); this.ranges.push(range);
}
}
}
regexpEscape(regexpString) {
return regexpString.replace(/[-\/\\^$*+?.()|\{}]/g, '\\$&'); }
getRanges(text) {
this.makeRanges(text.split(/\r?\n/));
return this.ranges;
}
updateDecorations(editor) {
let ranges = this.getRanges(editor.document.getText());
let decorationType = vscode.window.createTextEditorDecorationType({
"textDecoration": "underline " + this.underlineColor
});
editor.setDecorations(decorationType, ranges);
return decorationType;
}
getRangeAtCursor(editor) {
let ranges = this.getRanges(editor.document.getText());
let selection = editor.selection;
for (let i = 0; i < ranges.length; i++) {
if (this.isWithInRange(selection, range)) {
return range;
}
}
return null;
}
isWithInRange(selection, range) {
if (range.start.line == 44) {
console.log(selection, range);
}
var ret = true;
if (range.start.line > selection.start.line ||
range.end.line < selection.start.line) {
ret = false;
}
if (range.start.character >= selection.start.character ||
range.end.character <= selection.start.character) {
ret = false;
}
return ret;
}
buildQuickPick() {
var days = {};
for (var i = 1; i < 90; i++) {
let _cd = moment().add(i, "day");
let cd = _cd.format("YYYY-MM-DD");
if (dayscd == undefined) { }
if (i < 8) {
dayscd.push("Next" + _cd.format("dddd")); }
dayscd.push("+" + i + "day"); }
var items = [];
items = Object.keys(days).map(key => {
return { label: key, description: dayskey.join(", ") }; }).sort((a, b) => {
if (a.description.indexOf("Today") > -1) { return -1; }
return (a.label > b.label) ? 1 : -1;
});
return items;
}
// for MDTasks
indentLevel(line) {
return (/^(\s+)/.test(line)) ? RegExp.$1.length : 0;
}
findParentTasks(lines, startRow) {
let currentIndentLevel = this.indentLevel(linesstartRow); for (let i = startRow; i >= 0; i--) {
if (currentIndentLevel > this.indentLevel(linesi)) { return i;
}
}
return -1;
}
isChildTasksAllDone(edits, lines, parentRow) {
let firstChildRow = parentRow + 1;
for (let i = firstChildRow; i < lines.length; i++) {
if (childIndentLevel > this.indentLevel(line)) {
break;
}
if (childIndentLevel < this.indentLevel(line)) {
continue;
}
let child = edits.filter(d => { return d.range.start.line == i; });
if (child.length > 0 && child0.dist == "x") { } else if (child.length > 0 && child0.dist == " ") { return false;
} else if (/^\s*\-?\s?\\s\\s/.test(line)) { return false;
}
}
return true;
}
checkParentTasks(edits, lines) {
let parentRow = this.findParentTasks(lines, row);
if (parentRow == -1) {
return edits;
}
let parentDone = this.isChildTasksAllDone(edits, lines, parentRow);
this.makeRanges(lines);
let range = this.ranges.filter((d) => { return d.start.line == parentRow; });
let dist = "";
if (range.length != 0) {
dist = (parentDone) ? "x" : " "; edits.push({ range: range0, dist: dist }); }
if (parentRow != null && dist != "" && this.indentLevel(linesparentRow) != 0) { return this.checkParentTasks(edits, lines);
}
return edits;
}
findCheckboxAtCursorLine(line) {
let ranges = this.ranges.sort((a, b) => {
if (a.start.line > b.start.line) {
return 1;
} else if (a.start.line < b.start.line) {
return -1;
} else {
return (a.start.character > b.start.character) ? 1 : -1;
}
});
let filteredRange = ranges.filter(d => { return d.start.line == line; });
if (filteredRange.length == 0) {
return;
}
let position = new vscode.Position(
filteredRange0.start.line, filteredRange0.start.character + 1 );
return position;
}
};