Skip to content

Commit

Permalink
todo plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
WuTheFWasThat committed Apr 9, 2017
1 parent c80f89c commit f0ba825
Show file tree
Hide file tree
Showing 12 changed files with 245 additions and 22 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"eslint-plugin-react": "^6.4.1",
"express": "^4.14.0",
"file-loader": "^0.9.0",
"ignore-styles": "^5.0.1",
"mocha": "^3.0.2",
"node-sass": "^3.8.0",
"react-hot-loader": "^1.3.0",
Expand Down
6 changes: 0 additions & 6 deletions src/assets/js/configurations/vim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { motionKey } from '../keyDefinitions';
import { SINGLE_LINE_MOTIONS } from '../definitions/motions';
import Config from '../config';

// TODO: 'swap-case': [['~']]
// TODO: 'next-sentence': [[')']]
// TODO: 'prev-sentence': [['(']]

Expand Down Expand Up @@ -111,7 +110,6 @@ export const NORMAL_MODE_MAPPINGS: HotkeyMapping = Object.assign({
'swap-block-up': [['ctrl+k']],
'search-local': [['ctrl+/'], ['ctrl+f']],
'search-global': [['/']],
'toggle-strikethrough': [['ctrl+enter']],
'export-file': [['ctrl+s']],
'zoom-prev-sibling': [['alt+k']],
'zoom-next-sibling': [['alt+j']],
Expand Down Expand Up @@ -146,7 +144,6 @@ export const VISUAL_LINE_MODE_MAPPINGS: HotkeyMapping = Object.assign({
'visual-line-yank-clone': [['Y']],
'visual-line-indent': [['>'], ['tab'], ['ctrl+l']],
'visual-line-unindent': [['<'], ['shift+tab'], ['ctrl+h']],
'visual-line-toggle-strikethrough': [['ctrl+enter']],
'visual-line-swap-case': [['~']],
}, NORMAL_MOTION_MAPPINGS);

Expand Down Expand Up @@ -176,7 +173,6 @@ export const INSERT_MODE_MAPPINGS: HotkeyMapping = Object.assign({
'indent-blocks': [['tab']],
'swap-block-down': [],
'swap-block-up': [],
'toggle-strikethrough': [['ctrl+enter']],
'zoom-prev-sibling': [['alt+k']],
'zoom-next-sibling': [['alt+j']],
'zoom-in': [['ctrl+right']],
Expand Down Expand Up @@ -225,8 +221,6 @@ export const WORKFLOWY_MODE_MAPPINGS: HotkeyMapping = Object.assign({
'indent-blocks': [['tab']],
'swap-block-down': [['meta+shift+up']],
'swap-block-up': [['meta+shift+down']],
// NOTE: in workflowy, this also crosses out children
'toggle-strikethrough': [['meta+enter']],
'zoom-prev-sibling': [],
'zoom-next-sibling': [],
'zoom-in': [],
Expand Down
1 change: 1 addition & 0 deletions src/assets/js/keyDefinitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export type ActionContext = {
end_i: number,
start: Path,
end: Path,
selected: Array<Path>,
parent: Path,
num_rows: number,
},
Expand Down
1 change: 1 addition & 0 deletions src/assets/js/modes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ registerMode({
start: children[index1],
end: children[index2],
parent: parent,
selected: children.slice(index1, index2 + 1),
num_rows: (index2 - index1) + 1,
};
return context;
Expand Down
20 changes: 12 additions & 8 deletions src/assets/js/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -731,7 +731,10 @@ export default class Session extends EventEmitter {
return await this.document.getLength(this.cursor.row);
}

private async addChars(row: Row, col: Col, chars: Chars) {
public async addChars(row: Row, col: Col, chars: Chars) {
if (col < 0) {
col = (await this.document.getLength(row)) + col + 1;
}
await this.do(new mutations.AddChars(row, col, chars));
}

Expand All @@ -747,11 +750,12 @@ export default class Session extends EventEmitter {
await this.addChars(this.cursor.row, col, chars);
}

private async delChars(path: Path, col: Col, nchars: number, options: DelCharsOptions = {}) {
const n = await this.document.getLength(path.row);
public async delChars(row: Row, col: Col, nchars: number, options: DelCharsOptions = {}) {
const n = await this.document.getLength(row);
let deleted: Chars = [];
if (col < 0) { col = n + col; }
if ((n > 0) && (nchars > 0) && (col < n)) {
const mutation = new mutations.DelChars(path.row, col, nchars);
const mutation = new mutations.DelChars(row, col, nchars);
await this.do(mutation);
deleted = mutation.deletedChars;
if (options.yank) {
Expand All @@ -763,11 +767,11 @@ export default class Session extends EventEmitter {

public async delCharsBeforeCursor(nchars: number, options: DelCharsOptions = {}) {
nchars = Math.min(this.cursor.col, nchars);
return await this.delChars(this.cursor.path, this.cursor.col - nchars, nchars, options);
return await this.delChars(this.cursor.path.row, this.cursor.col - nchars, nchars, options);
}

public async delCharsAfterCursor(nchars: number, options: DelCharsOptions = {}) {
return await this.delChars(this.cursor.path, this.cursor.col, nchars, options);
return await this.delChars(this.cursor.path.row, this.cursor.col, nchars, options);
}

private async changeChars(row: Row, col: Col, nchars: number, change_fn: (chars: Chars) => Chars) {
Expand Down Expand Up @@ -817,7 +821,7 @@ export default class Session extends EventEmitter {
// yank as a row, not chars
await this.yankRowAtCursor();
}
return await this.delChars(this.cursor.path, 0, await this.curLineLength());
return await this.delChars(this.cursor.path.row, 0, await this.curLineLength());
}

public async yankChars(path: Path, col: Col, nchars: number) {
Expand Down Expand Up @@ -860,7 +864,7 @@ export default class Session extends EventEmitter {
[cursor1, cursor2] = [cursor2, cursor1];
}
const offset = options.includeEnd ? 1 : 0;
await this.delChars(cursor1.path, cursor1.col, cursor2.col - cursor1.col + offset, options);
await this.delChars(cursor1.path.row, cursor1.col, cursor2.col - cursor1.col + offset, options);
}

public async newLineBelow(
Expand Down
1 change: 1 addition & 0 deletions src/assets/js/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const default_settings: SettingsType = {
theme: 'default-theme',
showKeyBindings: true,
hotkeys: {},
// TODO import these names from the plugins
enabledPlugins: ['Marks', 'HTML', 'LaTeX', 'Text Formatting', 'Todo'],
};

Expand Down
4 changes: 1 addition & 3 deletions src/plugins/marks/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ export class MarksPlugin {

// NOTE: because listing marks filters, disabling is okay

const pluginName = 'Marks';
export const pluginName = 'Marks';

registerPlugin<MarksPlugin>(
{
Expand All @@ -578,5 +578,3 @@ registerPlugin<MarksPlugin>(
},
(api) => api.deregisterAll(),
);

export { pluginName };
23 changes: 18 additions & 5 deletions src/plugins/text_formatting/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,28 @@ registerPlugin<void>(
return tokenizer;
}
return tokenizer.then(RegexTokenizerModifier<React.ReactNode>(
matchWordRegex('\\*\\*(\\n|.)+?\\*\\*'),
hideBorderAndModify(2, 2, (char_info) => { char_info.renderOptions.classes[boldClass] = true; })
// triple asterisk means both bold and italic
matchWordRegex('\\*\\*\\*(\\n|.)+?\\*\\*\\*'),
hideBorderAndModify(3, 3, (char_info) => {
char_info.renderOptions.classes[italicsClass] = true;
char_info.renderOptions.classes[boldClass] = true;
})
)).then(RegexTokenizerModifier<React.ReactNode>(
// middle is either a single character, or both sides have a non-* character
matchWordRegex('\\*((\\n|[^\\*])|[^\\*](\\n|.)+?[^\\*])?\\*'),
hideBorderAndModify(1, 1, (char_info) => { char_info.renderOptions.classes[italicsClass] = true; })
hideBorderAndModify(1, 1, (char_info) => {
char_info.renderOptions.classes[italicsClass] = true;
})
)).then(RegexTokenizerModifier<React.ReactNode>(
matchWordRegex('\\*\\*(\\n|.)+?\\*\\*'),
hideBorderAndModify(2, 2, (char_info) => {
char_info.renderOptions.classes[boldClass] = true;
})
)).then(RegexTokenizerModifier<React.ReactNode>(
matchWordRegex('_(\\n|.)+?_'),
hideBorderAndModify(1, 1, (char_info) => { char_info.renderOptions.classes[underlineClass] = true; })
matchWordRegex('(?:[\\*]*)_(\\n|.)+?_(?:[\\*]*)'),
hideBorderAndModify(1, 1, (char_info) => {
char_info.renderOptions.classes[underlineClass] = true;
})
));
});
},
Expand Down
3 changes: 3 additions & 0 deletions src/plugins/todo/index.sass
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.strikethrough
text-decoration: line-through
opacity: 0.5
121 changes: 121 additions & 0 deletions src/plugins/todo/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import * as _ from 'lodash';

import './index.sass';

import { hideBorderAndModify, RegexTokenizerModifier } from '../../assets/js/utils/token_unfolder';
import { registerPlugin } from '../../assets/js/plugins';
import { matchWordRegex } from '../../assets/js/utils';
import { Row } from '../../assets/js/types';
import Session from '../../assets/js/session';

const strikethroughClass = 'strikethrough';

export const pluginName = 'Todo';

registerPlugin<void>(
{
name: pluginName,
author: 'Jeff Wu',
description: `Lets you strike out bullets`,
},
function(api) {
api.registerHook('session', 'renderLineTokenHook', (tokenizer, hooksInfo) => {
if (hooksInfo.has_cursor) {
return tokenizer;
}
if (hooksInfo.has_highlight) {
return tokenizer;
}
return tokenizer.then(RegexTokenizerModifier<React.ReactNode>(
matchWordRegex('\\~\\~(\\n|.)+?\\~\\~'),
hideBorderAndModify(2, 2, (char_info) => { char_info.renderOptions.classes[strikethroughClass] = true; })
));
});

async function isStruckThrough(session: Session, row: Row) {
const text = await session.document.getText(row);
return (text.slice(0, 2) === '~~') && (text.slice(-2) === '~~');
}

async function addStrikeThrough(session: Session, row: Row) {
await session.addChars(row, -1, ['~', '~']);
await session.addChars(row, 0, ['~', '~']);
}

async function removeStrikeThrough(session: Session, row: Row) {
await session.delChars(row, -2, 2);
await session.delChars(row, 0, 2);
}

api.registerAction(
'toggle-strikethrough',
'Toggle strikethrough for a row',
async function({ session }) {
if (await isStruckThrough(session, session.cursor.row)) {
await removeStrikeThrough(session, session.cursor.row);
} else {
await addStrikeThrough(session, session.cursor.row);
}
},
);

// TODO: this should maybe strikethrough children, since UI suggests it?
api.registerAction(
'visual-line-toggle-strikethrough',
'Toggle strikethrough for rows',
async function({ session, visual_line }) {
if (visual_line == null) {
throw new Error('Visual_line mode arguments missing');
}

const is_struckthrough = await Promise.all(
visual_line.selected.map(async (path) => {
return await isStruckThrough(session, path.row);
})
);
if (_.every(is_struckthrough)) {
await Promise.all(
visual_line.selected.map(async (path) => {
await removeStrikeThrough(session, path.row);
})
);
} else {
await Promise.all(
visual_line.selected.map(async (path, i) => {
if (!is_struckthrough[i]) {
await addStrikeThrough(session, path.row);
}
})
);
}
await session.setMode('NORMAL');
},
);

api.registerDefaultMappings(
'NORMAL',
{
'toggle-strikethrough': [['ctrl+enter']],
},
);

api.registerDefaultMappings(
'INSERT',
{
'toggle-strikethrough': [['ctrl+enter', 'meta+enter']],
},
);

api.registerDefaultMappings(
'VISUAL_LINE',
{
'visual-line-toggle-strikethrough': [['ctrl+enter']],
},
);

// TODO for workflowy mode
// NOTE: in workflowy, this also crosses out children
// 'toggle-strikethrough': [['meta+enter']],
},
(api => api.deregisterAll()),
);
1 change: 1 addition & 0 deletions test/mocha.opts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
--require ts-node/register
--require ignore-styles
--watch-extensions tsx,ts
--timeout 60000
test/tests/*.ts
85 changes: 85 additions & 0 deletions test/tests/todo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/* globals describe, it */
import TestCase from '../testcase';
import * as Todo from '../../src/plugins/todo';
import '../../src/assets/js/plugins';

const toggleStrikethroughKey = 'ctrl+enter';

describe('todo', function() {
it('works in basic case', async function() {
let t = new TestCase([
'a line',
'another line',
], {plugins: [Todo.pluginName]});
t.sendKey(toggleStrikethroughKey);
t.expect([
'~~a line~~',
'another line',
]);

t.sendKey(toggleStrikethroughKey);
t.expect([
'a line',
'another line',
]);

t.sendKey('u');
t.expect([
'~~a line~~',
'another line',
]);

t.sendKey('u');
t.expect([
'a line',
'another line',
]);
await t.done();
});

it('works in visual line', async function() {
let t = new TestCase([
'a line',
'~~another line~~',
], {plugins: [Todo.pluginName]});
t.sendKeys('Vj');
t.sendKey(toggleStrikethroughKey);
t.expect([
'~~a line~~',
'~~another line~~',
]);

t.sendKeys('Vk');
t.sendKey(toggleStrikethroughKey);
t.expect([
'a line',
'another line',
]);

t.sendKeys('Vj');
t.sendKey(toggleStrikethroughKey);
t.expect([
'~~a line~~',
'~~another line~~',
]);

t.sendKey('u');
t.expect([
'a line',
'another line',
]);

t.sendKey('u');
t.expect([
'~~a line~~',
'~~another line~~',
]);

t.sendKey('u');
t.expect([
'a line',
'~~another line~~',
]);
await t.done();
});
});

0 comments on commit f0ba825

Please sign in to comment.