Skip to content

Commit d1bfcfb

Browse files
committed
Merge pull request #8 from lumaxis/feature/refactoring
Chore: Refactor code and update files organization
2 parents 5813238 + 308c13e commit d1bfcfb

File tree

10 files changed

+218
-118
lines changed

10 files changed

+218
-118
lines changed

.vscode/launch.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"request": "launch",
2626
"runtimeExecutable": "${execPath}",
2727
"args": [
28+
"--disable-extensions",
2829
"--extensionDevelopmentPath=${workspaceFolder}",
2930
"--extensionTestsPath=${workspaceFolder}/out/test/suite/index"
3031
],

.vscode/settings.json

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
// Place your settings in this file to overwrite default and user settings.
22
{
3-
"files.exclude": {
4-
"out": false // set this to true to hide the "out" folder with the compiled JS files
5-
},
6-
"search.exclude": {
7-
"out": true // set this to false to include "out" folder in search results
8-
},
9-
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
10-
"typescript.tsc.autoDetect": "off"
11-
}
3+
"files.exclude": {
4+
"out": false // set this to true to hide the "out" folder with the compiled JS files
5+
},
6+
"search.exclude": {
7+
"out": true // set this to false to include "out" folder in search results
8+
},
9+
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
10+
"typescript.tsc.autoDetect": "off",
11+
"editor.codeActionsOnSave": {
12+
"source.organizeImports": true
13+
}
14+
}

src/extension.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,12 @@
11
import * as vscode from 'vscode';
2-
import { generateCopyableText } from './lib/textHelpers';
2+
import { ExtensionContext } from 'vscode';
3+
import { generateSnippet } from './lib/textHelpers';
34

4-
export function activate(context: vscode.ExtensionContext) {
5+
export function activate(context: ExtensionContext) {
56
let disposable = vscode.commands.registerTextEditorCommand('snippet-copy.copyWithoutLeadingIndentation', async (editor) => {
6-
if (editor.selections.length === 1) {
7-
const document = editor.document;
8-
if (!document) { return; };
7+
const snippet = generateSnippet(editor.document, editor.selections);
98

10-
const text = generateCopyableText(document, editor.selection);
11-
12-
if (!text) { return; };
13-
14-
await vscode.env.clipboard.writeText(text);
15-
} else {
16-
// TODO: Handle error case
17-
vscode.window.showWarningMessage('Copying without leading indentation from multiple selections is currently not supported');
18-
}
9+
await vscode.env.clipboard.writeText(snippet);
1910
});
2011

2112
context.subscriptions.push(disposable);

src/lib/documentHelpers.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { EndOfLine, Range, TextDocument, TextLine } from "vscode";
2+
3+
export function linesForIndexes(document: TextDocument, lineIndexes: number[]): TextLine[] {
4+
return lineIndexes.map((lineIndex) => {
5+
return document.lineAt(lineIndex);
6+
});
7+
}
8+
9+
export function minimumIndentationForLineIndexes(document: TextDocument, lineIndexes: number[]): number {
10+
const indentationLevels = lineIndexes.map((lineIndex) => {
11+
return document.lineAt(lineIndex).firstNonWhitespaceCharacterIndex;
12+
});
13+
14+
const minimumIndentationLevelInSelection = Math.min(...indentationLevels);
15+
return minimumIndentationLevelInSelection;
16+
}
17+
18+
export function contentOfLinesWithAdjustedIndentation(document: TextDocument, lineIndexes: number[], minimumIndentation: number): string {
19+
const lines = linesForIndexes(document, lineIndexes);
20+
const contentOfLinesWithAdjustedIndentation = lines.map((line) => {
21+
const adjustedRange = adjustedRangeWithMinimumIndentation(line.range, minimumIndentation);
22+
return document.getText(adjustedRange);
23+
});
24+
25+
const eolCharacter = endOfLineCharacter(document);
26+
return contentOfLinesWithAdjustedIndentation.join(eolCharacter);
27+
}
28+
29+
export function adjustedRangeWithMinimumIndentation(range: Range, minimumIndentation: number): Range {
30+
if (range.start.character !== 0) {
31+
console.warn('Adjusting range: Range does not start at character 0, this is not expected.');
32+
}
33+
34+
const adjustedRange = new Range(range.start.line, range.start.character + minimumIndentation, range.end.line, range.end.character);
35+
return adjustedRange;
36+
}
37+
38+
export function endOfLineCharacter(document: TextDocument): string {
39+
return document.eol === EndOfLine.CRLF ? '\r\n' : '\n';
40+
}

src/lib/selectionHelpers.ts

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,9 @@
1-
import { Selection, TextDocument, Range, EndOfLine } from "vscode";
1+
import { Selection } from "vscode";
22

3-
export function calculateSelectionLineIndexes(selection: Selection): number[] {
4-
const selectionStart = selection.start;
5-
const selectionEnd = selection.end;
6-
7-
const lineIndexes = Array.from({ length: 1 + (selectionEnd.line - selectionStart.line) }, (_, k) => k + selectionStart.line);
3+
export function lineIndexesForSelection(selection: Selection): number[] {
4+
const numberOfLinesInSelection = 1 + (selection.end.line - selection.start.line);
5+
const lineIndexes = Array.from({ length: numberOfLinesInSelection }, (_, k) => k + selection.start.line);
86

97
return lineIndexes;
108
}
119

12-
export function calculateMinimumIndentationInSelection(document: TextDocument, lineIndexes: number[]): number {
13-
const indentationLevels = lineIndexes.map((lineIndex) => {
14-
return document.lineAt(lineIndex).firstNonWhitespaceCharacterIndex;
15-
});
16-
17-
const minimumIndentationLevelInSelection = Math.min(...indentationLevels);
18-
return minimumIndentationLevelInSelection;
19-
}
20-
21-
export function adjustIndentationInSelection(document: TextDocument, lineIndexes: number[], minimumIndentation: number) {
22-
const endOfLineCharacter = document.eol === EndOfLine.CRLF ? '\r\n' : '\n';
23-
24-
return lineIndexes.map((lineIndex) => {
25-
const line = document.lineAt(lineIndex);
26-
const range = line.range;
27-
const lineStart = range.start.character;
28-
const lineEnd = range.end.character;
29-
30-
return document.getText(new Range(lineIndex, lineStart + minimumIndentation, lineIndex, lineEnd));
31-
}).join(endOfLineCharacter);
32-
}

src/lib/textHelpers.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,28 @@
1-
import { TextDocument, Selection } from "vscode";
2-
import { calculateSelectionLineIndexes, calculateMinimumIndentationInSelection, adjustIndentationInSelection } from "./selectionHelpers";
1+
import { Selection, TextDocument } from "vscode";
2+
import { contentOfLinesWithAdjustedIndentation, endOfLineCharacter, minimumIndentationForLineIndexes } from "./documentHelpers";
3+
import { lineIndexesForSelection } from "./selectionHelpers";
4+
5+
export function generateSnippet(document: TextDocument, selections: Selection[]): string {
6+
let texts: string[] = [];
7+
selections.forEach(selection => {
8+
texts.push(generateCopyableText(document, selection));
9+
});
10+
11+
const snippet = texts.join(endOfLineCharacter(document));
12+
13+
return snippet;
14+
}
315

416
export function generateCopyableText(document: TextDocument, selection: Selection) {
5-
const lineIndexes = calculateSelectionLineIndexes(selection);
17+
const lineIndexes = lineIndexesForSelection(selection);
618

19+
// Remove last line's index if there's no selected text on that line
720
if (lineIndexes.length > 1 && selection.end.character === 0) {
821
lineIndexes.pop();
922
}
1023

11-
const minimumIndentation = calculateMinimumIndentationInSelection(document, lineIndexes);
12-
const text = adjustIndentationInSelection(document, lineIndexes, minimumIndentation);
24+
const minimumIndentation = minimumIndentationForLineIndexes(document, lineIndexes);
25+
const text = contentOfLinesWithAdjustedIndentation(document, lineIndexes, minimumIndentation);
1326

1427
return text;
1528
}
Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1-
function doSomething(aValue) {
2-
if (aValue) {
3-
console.log(`Doing something with ${aValue}!`);
1+
class MyThing {
2+
doSomething(aValue) {
3+
if (aValue) {
4+
console.log(`Doing something with ${aValue}!`);
5+
}
6+
}
7+
8+
doSomethingElse() {
9+
throw new Error('Nope!');
410
}
511
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import * as assert from 'assert';
2+
import * as path from 'path';
3+
import * as vscode from 'vscode';
4+
import { Range, TextDocument } from 'vscode';
5+
import { adjustedRangeWithMinimumIndentation, contentOfLinesWithAdjustedIndentation, endOfLineCharacter, linesForIndexes, minimumIndentationForLineIndexes } from '../../../lib/documentHelpers';
6+
7+
8+
const fixturesPath = '/../../../../src/test/fixtures/';
9+
const uri = vscode.Uri.file(
10+
path.join(__dirname + fixturesPath + 'javascript-example.js')
11+
);
12+
13+
describe('Document Helpers', function () {
14+
let document: TextDocument;
15+
16+
before(async () => {
17+
document = await vscode.workspace.openTextDocument(uri);
18+
});
19+
20+
context('linesForIndexes', () => {
21+
it('returns the correct lines', () => {
22+
const lines = linesForIndexes(document, [0, 1]);
23+
assert.equal(lines.length, 2);
24+
assert.equal(lines[0].lineNumber, 0);
25+
assert.equal(lines[0].text, 'class MyThing {');
26+
assert.equal(lines[1].lineNumber, 1);
27+
assert.equal(lines[1].text, ' doSomething(aValue) {');
28+
});
29+
});
30+
31+
context('minimumIndentationLevelForLineIndexes', async () => {
32+
it('calculates the correct minimum indentation level for a single line', () => {
33+
assert.equal(minimumIndentationForLineIndexes(document, [3]), 6);
34+
});
35+
36+
it('calculates the correct minimum indentation level for multiple lines', () => {
37+
assert.equal(minimumIndentationForLineIndexes(document, [1, 2, 3]), 2);
38+
});
39+
});
40+
41+
context('contentOfLinesWithAdjustedIndentation', async () => {
42+
it('returns multiline text with the indentation adjusted correctly', () => {
43+
assert.equal(contentOfLinesWithAdjustedIndentation(document, [2, 3, 4], 4), 'if (aValue) {\n console.log(`Doing something with ${aValue}!`);\n}');
44+
});
45+
46+
it('returns single line text with the indentation adjusted correctly', () => {
47+
assert.equal(contentOfLinesWithAdjustedIndentation(document, [3], 6), 'console.log(`Doing something with ${aValue}!`);');
48+
});
49+
50+
it('returns text with CRLF characters if file is using them', async () => {
51+
const uri = vscode.Uri.file(
52+
path.join(__dirname + fixturesPath + 'crlf-ruby-example.rb')
53+
);
54+
let crlfDocument = await vscode.workspace.openTextDocument(uri);
55+
56+
assert.equal(contentOfLinesWithAdjustedIndentation(crlfDocument, [1, 2, 3], 2), 'def polish\r\n puts "Polishing"\r\nend');
57+
});
58+
});
59+
60+
context('adjustedRangeWithMinimumIndentation', () => {
61+
it('adjusts the range', () => {
62+
const adjustedRange = adjustedRangeWithMinimumIndentation(new Range(2, 0, 2, 17), 4);
63+
assert.equal(adjustedRange.start.line, 2);
64+
assert.equal(adjustedRange.start.character, 4);
65+
assert.equal(adjustedRange.end.line, 2);
66+
assert.equal(adjustedRange.end.character, 17);
67+
});
68+
});
69+
70+
context('endOfLineCharacter', () => {
71+
it('correctly returns LF', async () => {
72+
assert.equal(endOfLineCharacter(document), '\n');
73+
});
74+
75+
it('correctly returns CRLF', async () => {
76+
const uri = vscode.Uri.file(
77+
path.join(__dirname + fixturesPath + 'crlf-ruby-example.rb')
78+
);
79+
assert.equal(endOfLineCharacter(await vscode.workspace.openTextDocument(uri)), '\r\n');
80+
});
81+
});
82+
});
Lines changed: 6 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,24 @@
11
import * as assert from 'assert';
2-
import * as path from 'path';
3-
import * as vscode from 'vscode';
2+
import { Selection } from 'vscode';
3+
import { lineIndexesForSelection } from '../../../lib/selectionHelpers';
44

5-
import { calculateSelectionLineIndexes, calculateMinimumIndentationInSelection, adjustIndentationInSelection } from '../../../lib/selectionHelpers';
6-
// import * as myExtension from '../extension';
7-
8-
const fixturesPath = '/../../../../src/test/fixtures/';
9-
const uri = vscode.Uri.file(
10-
path.join(__dirname + fixturesPath + 'javascript-example.js')
11-
);
125

136
describe('Selection Helpers', function () {
147
context('calculateSelectionLineIndexes', () => {
158
it('calculates the correct line indexes from an empty selection', () => {
16-
assert.deepEqual([0], calculateSelectionLineIndexes(new vscode.Selection(0, 0, 0, 0)));
9+
assert.deepEqual([0], lineIndexesForSelection(new Selection(0, 0, 0, 0)));
1710
});
1811

1912
it('calculates the correct line indexes from a single line selection', () => {
20-
assert.deepEqual([1], calculateSelectionLineIndexes(new vscode.Selection(1, 2, 1, 15)));
13+
assert.deepEqual([1], lineIndexesForSelection(new Selection(1, 2, 1, 15)));
2114
});
2215

2316
it('calculates the correct line indexes from a multiline selection', () => {
24-
assert.deepEqual([1, 2, 3], calculateSelectionLineIndexes(new vscode.Selection(1, 2, 3, 3)));
17+
assert.deepEqual([1, 2, 3], lineIndexesForSelection(new Selection(1, 2, 3, 3)));
2518
});
2619

2720
it('calculates the correct line indexes from an reversed selection ', () => {
28-
assert.deepEqual([1, 2, 3], calculateSelectionLineIndexes(new vscode.Selection(3, 0, 1, 0)));
29-
});
30-
});
31-
32-
context('calculateMinimumIndentationInSelection', async () => {
33-
let document: vscode.TextDocument;
34-
35-
before(async () => {
36-
document = await vscode.workspace.openTextDocument(uri);
37-
});
38-
39-
it('calculates the correct minimum indentation level for a single line', () => {
40-
assert.equal(4, calculateMinimumIndentationInSelection(document, [2]));
41-
});
42-
43-
it('calculates the correct minimum indentation level for multiple lines', () => {
44-
assert.equal(2, calculateMinimumIndentationInSelection(document, [1, 2, 3]));
45-
});
46-
});
47-
48-
context('adjustIndentationInSelection', async () => {
49-
let document: vscode.TextDocument;
50-
51-
before(async () => {
52-
document = await vscode.workspace.openTextDocument(uri);
53-
});
54-
55-
it('returns multiline text with the indentation adjusted correctly', () => {
56-
assert.equal('if (aValue) {\n console.log(`Doing something with ${aValue}!`);\n}', adjustIndentationInSelection(document, [1, 2, 3], 2));
57-
});
58-
59-
it('returns single line text with the indentation adjusted correctly', () => {
60-
assert.equal('console.log(`Doing something with ${aValue}!`);', adjustIndentationInSelection(document, [2], 4));
61-
});
62-
63-
it('returns text with CRLF characters if file is using them', async () => {
64-
const uri = vscode.Uri.file(
65-
path.join(__dirname + fixturesPath + 'crlf-ruby-example.rb')
66-
);
67-
let crlfDocument = await vscode.workspace.openTextDocument(uri);
68-
69-
assert.equal('def polish\r\n puts "Polishing"\r\nend', adjustIndentationInSelection(crlfDocument, [1, 2, 3], 2));
21+
assert.deepEqual([1, 2, 3], lineIndexesForSelection(new Selection(3, 0, 1, 0)));
7022
});
7123
});
7224
});

0 commit comments

Comments
 (0)