Skip to content

Commit

Permalink
#8 separator rows are skipped when navigating
Browse files Browse the repository at this point in the history
  • Loading branch information
rpeshkov authored Apr 7, 2018
1 parent b29fd01 commit f0612e8
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 47 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

### Changed

- Navigation now via `Tab` and `Shift`+`Tab` skips separator rows

## [0.0.2] - 2018-04-05

### Added
Expand Down
12 changes: 10 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ export function activate(ctx: vscode.ExtensionContext) {
if (table) {
const nav = new TableNavigator(table);
const newPos = nav.nextCell(editor.selection.start);
editor.selection = new vscode.Selection(newPos, newPos);
if (newPos) {
editor.selection = new vscode.Selection(newPos, newPos);
}

}
}
}));
Expand All @@ -65,7 +68,9 @@ export function activate(ctx: vscode.ExtensionContext) {
if (table) {
const nav = new TableNavigator(table);
const newPos = nav.previousCell(editor.selection.start);
editor.selection = new vscode.Selection(newPos, newPos);
if (newPos) {
editor.selection = new vscode.Selection(newPos, newPos);
}
}
}
}));
Expand Down Expand Up @@ -147,6 +152,9 @@ function formatAndGetTableUnderCursor(editor: vscode.TextEditor): Table | undefi
if (isUndefined(table)) {
return undefined;
}

table.startLine = tableRange.start.line;

const newText = stringifier.stringify(table);
editor.edit(b => b.replace(tableRange, newText));
return table;
Expand Down
113 changes: 82 additions & 31 deletions src/ttTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ export interface ColDef {
}

export class Table {
/**
* Line where the table starts
*/
startLine = 0;

rows: RowDef[] = [];
cols: ColDef[] = [];

Expand Down Expand Up @@ -82,47 +87,93 @@ export interface LineReader {
lineCount: number;
}

interface Pos {
start: number;
end: number;
class JumpPosition {
constructor(start: vscode.Position, end: vscode.Position, public isSeparator: boolean, prev?: JumpPosition) {
this.range = new vscode.Range(start, end);

if (prev) {
prev.next = this;
this.prev = prev;
}
}

range: vscode.Range;
next?: JumpPosition;
prev?: JumpPosition;
}

export class TableNavigator {
constructor(
public table: Table) { }
private jumpPositions: JumpPosition[] = [];

nextCell(cursorPosition: vscode.Position): vscode.Position {
if (cursorPosition.character === 0) {
return cursorPosition.translate(0, 2);
}
constructor(public table: Table) {
this.jumpPositions = this.buildJumpPositions();
}

const charPos = cursorPosition.character;
const colPos = this.getColumnsPosition();
const colIndex = colPos.findIndex(x => charPos >= x.start && charPos < x.end);
if (colIndex === this.table.cols.length - 1) {
return new vscode.Position(cursorPosition.line + 1, 2);
} else {
return new vscode.Position(cursorPosition.line, colPos[colIndex + 1].start + 1);
}
nextCell(cursorPosition: vscode.Position): vscode.Position | undefined {
return this.jump(cursorPosition, x => x.next!);
}

previousCell(cursorPosition: vscode.Position): vscode.Position | undefined {
return this.jump(cursorPosition, x => x.prev!);
}

previousCell(cursorPosition: vscode.Position): vscode.Position {
const charPos = cursorPosition.character;
const colPos = this.getColumnsPosition();
const colIndex = colPos.findIndex(x => charPos >= x.start && charPos < x.end);
if (colIndex <= 0) {
return new vscode.Position(cursorPosition.line - 1, colPos[colPos.length - 1].start + 1);
private jump(currentPosition: vscode.Position, accessor: (x: JumpPosition) => JumpPosition): vscode.Position | undefined {
let jmp = this.jumpPositions.find(x => x.range.contains(currentPosition));
if (jmp) {
jmp = accessor(jmp);
if (jmp) {
if (jmp.isSeparator) {
if (!accessor(jmp)) {
return undefined;
}
jmp = accessor(jmp);
}
return jmp.range.start.translate(0, 1);
}
}

// Maybe we're just outside left part of table? Let's move cursor a bit...
if (currentPosition.character === 0) {
return currentPosition.translate(0, 2);
} else {
return new vscode.Position(cursorPosition.line, colPos[colIndex - 1].start + 1);
return undefined;
}
}

private getColumnsPosition(): Pos[] {
let counter = 1;
return this.table.cols.reduce((prev: Pos[], cur) => {
prev.push({start: counter, end: counter + cur.width + 2});
counter += cur.width + 3;
return prev;
}, []);
private buildJumpPositions(): JumpPosition[] {
const result: JumpPosition[] = [];

const cellPadding = 2;
let lastAnchor = 0;
const anchors = this.table.cols.reduce((accum, col) => {
lastAnchor += col.width + cellPadding + 1;
accum.push(lastAnchor);
return accum;
}, [lastAnchor]);

for (let i = 0; i < this.table.rows.length; ++i) {
const row = this.table.rows[i];
const rowLine = this.table.startLine + i;

if (row.type === RowType.Separator) {
const prevJmpPos = result[result.length - 1];
// Extend last range to whole separator line or start from beginning of line
const start = prevJmpPos
? prevJmpPos.range.end
: new vscode.Position(rowLine, 0);
const end = start.translate(1);
const jmpPos = new JumpPosition(start, end, true, prevJmpPos);
result.push(jmpPos);
} else {
for (let j = 0; j < anchors.length - 1; ++j) {
const prevJmpPos = result[result.length - 1];
const start = new vscode.Position(rowLine, anchors[j] + 1);
const end = new vscode.Position(rowLine, anchors[j + 1]);
const jmpPos = new JumpPosition(start, end, false, prevJmpPos);
result.push(jmpPos);
}
}
}
return result;
}
}
51 changes: 37 additions & 14 deletions test/table-navigator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ suite('TableNavigator', () => {

setup(() => {
table = new Table();
table.addRow(RowType.Data, ['Value1', 'Value2']);
table.addRow(RowType.Data, ['Value3', 'Value4']);
table.addRow(RowType.Data, ['Column 1', 'Column 2']);
table.addRow(RowType.Separator, ['', '']);
table.addRow(RowType.Data, ['1', '2']);
table.addRow(RowType.Data, ['3', '4']);
table.addRow(RowType.Separator, ['', '']);

navigator = new TableNavigator(table);
});
Expand All @@ -22,38 +25,58 @@ suite('TableNavigator', () => {
test('should select first column when cursor in the beginning of line', () => {
const pos = new vscode.Position(0, 0);
const newPos = navigator.nextCell(pos);
assert.equal(newPos.line, 0);
assert.equal(newPos.character, 2);

assert.equal(newPos!.line, 0);
assert.equal(newPos!.character, 2);
});

test('should navigate next cell', () => {
const pos = new vscode.Position(0, 2);
const newPos = navigator.nextCell(pos);
assert.equal(newPos.line, 0);
assert.equal(newPos.character, 11);
assert.equal(newPos!.line, 0);
assert.equal(newPos!.character, 13);
});

test('should jump to next row', () => {
const pos = new vscode.Position(0, 11);
const pos = new vscode.Position(2, 13);
const newPos = navigator.nextCell(pos);
assert.equal(newPos!.line, 3);
assert.equal(newPos!.character, 2);
});

test('should skip separator row', () => {
const pos = new vscode.Position(0, 13);
const newPos = navigator.nextCell(pos);
assert.equal(newPos!.line, 2);
assert.equal(newPos!.character, 2);
});

test('should not move if cursor is on separator and it\'s the last line', () => {
const pos = new vscode.Position(4, 13);
const newPos = navigator.nextCell(pos);
assert.equal(newPos, undefined);
});

test('should not move if cursor in last cell and there is separator line below', () => {
const pos = new vscode.Position(3, 13);
const newPos = navigator.nextCell(pos);
assert.equal(newPos.line, 1);
assert.equal(newPos.character, 2);
assert.equal(newPos, undefined);
});
});

suite('previousCell', () => {
test('should navigate previous cell', () => {
const pos = new vscode.Position(0, 11);
const pos = new vscode.Position(0, 13);
const newPos = navigator.previousCell(pos);
assert.equal(newPos.line, 0);
assert.equal(newPos.character, 2);
assert.equal(newPos!.line, 0);
assert.equal(newPos!.character, 2);
});

test('should jump to prev row', () => {
const pos = new vscode.Position(1, 2);
const newPos = navigator.previousCell(pos);
assert.equal(newPos.line, 0);
assert.equal(newPos.character, 11);
assert.equal(newPos!.line, 0);
assert.equal(newPos!.character, 13);
});
});
});

0 comments on commit f0612e8

Please sign in to comment.