Skip to content

Commit ab8d9fa

Browse files
authored
Merge pull request #127 from docs/lucascosti/refactor-and-whitespace
Refactor regex code, add liquid whitespace control compatibility
2 parents cd4fced + 218ddea commit ab8d9fa

6 files changed

+125
-86
lines changed

CHANGELOG.md

+9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Change log
22

3+
## 1.6.0
4+
2 December 2022
5+
6+
Major refactor of the code by @lucascosti to facilitate future changes to this extension.
7+
8+
This version:
9+
- Adds the ability for the extension to open reusables/feature versioning tags that contain whitespace control.
10+
- Consolidates various regular expressions to improve code maintainability.
11+
312
## 1.5.2
413
14 September 2022
514

copier.js

+7-55
Original file line numberDiff line numberDiff line change
@@ -6,68 +6,20 @@ function copyMain() {
66
var editor = vscode.window.activeTextEditor;
77
shared.testNoTab(editor);
88

9-
var reusableString = shared.getReusableString(editor);
9+
var reusableString = shared.getThingString(editor);
1010

1111
if (reusableString != "") {
1212
// console.log('reusableString = ' + reusableString);
1313

1414
// Write to clipboard
1515
vscode.env.clipboard.writeText(reusableString);
16-
/*
17-
// Check what's in the clipboard now:
18-
vscode.env.clipboard.readText().then((text)=>{
19-
let clipboard_content = text;
20-
console.log('clipboard_content = ' + clipboard_content);
21-
});
22-
*/
23-
24-
var startSelection, endSelection, moveLeftBy, moveRightBy;
25-
startSelection = endSelection = editor.document.offsetAt(editor.selection.anchor);
26-
// Get the position of the start of the reusable
27-
// Do this by parsing each character from the cursor leftwards
28-
// until reaching "{" (for variables/reusables), or quotes or
29-
// a colon (:) for article feature flag versioning
30-
for (let parseCharacter = ""; !parseCharacter.match(/{|'|"|:/); startSelection--) {
31-
let startPosition = editor.document.positionAt(startSelection);
32-
let stopPosition = editor.document.positionAt(startSelection+1);
33-
let textRange = new vscode.Range(startPosition, stopPosition);
34-
parseCharacter = editor.document.getText(textRange);
35-
// console.log('startSelection = ' + startSelection + '; parseCharacter = ' + parseCharacter);
36-
// If it's an article feature flag versioning, move rightwards one or two.
37-
if (parseCharacter.match(/'|"/)) { startSelection++; }
38-
else if (parseCharacter.match(/:/)) { startSelection = startSelection + 2; }
39-
moveLeftBy = endSelection - startSelection;
40-
// console.log('move left by = ' + moveLeftBy);
41-
}
42-
// Get the position of the end of the reusable by parsing forwards
43-
// until reaching "}" (for variables/reusables), or quotes or
44-
// a line end for article feature flag versioning
45-
for (let parseCharacter = ""; !parseCharacter.match(/}|'|"|\n/); endSelection++) {
46-
let startPosition = editor.document.positionAt(endSelection);
47-
let stopPosition = editor.document.positionAt(endSelection+1);
48-
let textRange = new vscode.Range(startPosition, stopPosition);
49-
parseCharacter = editor.document.getText(textRange);
50-
// console.log('endSelection = ' + endSelection + '; parseCharacter = ' + parseCharacter);
51-
// If it's an article feature flag versioning, move leftwards one.
52-
if (parseCharacter.match(/'|"|\n/)) { endSelection--; }
53-
moveRightBy = endSelection - startSelection;
54-
// console.log('move left by = ' + moveRightBy);
55-
}
56-
57-
// Move the cursor to the start of the reusable
58-
vscode.commands.executeCommand("cursorMove", {
59-
to: "left",
60-
by: "character",
61-
value: moveLeftBy
62-
})
63-
// Move the cursor to the end of the reusable, selecting the text moved over
64-
vscode.commands.executeCommand("cursorMove", {
65-
to: "right",
66-
by: "character",
67-
value: moveRightBy,
68-
select: true
69-
})
7016

17+
// Get the range of the reusableString
18+
let stringRegex = new RegExp(reusableString, 'g');
19+
let reusableStringRange = editor.document.getWordRangeAtPosition(editor.selection.active, stringRegex);
20+
21+
// set the selection to range of the reusableString
22+
editor.selection = new vscode.Selection(reusableStringRange.start, reusableStringRange.end);
7123
}
7224
else {
7325
vscode.window.showInformationMessage("Cursor is not within a reusable, variable, or feature flag.");

open-reusables-1.6.0.vsix

238 KB
Binary file not shown.

opener.js

+15-23
Original file line numberDiff line numberDiff line change
@@ -12,33 +12,23 @@ function openMain() {
1212
return;
1313
}
1414

15+
// Use the current selection or cursor, and get the type (i.e. a reusable/feature flag) and value (i.e. the reusable path or the feature flag name)
1516
var selection = editor.selection;
1617
if (selection.isEmpty) {
17-
var selectedString = shared.getReusableString(editor);
18-
// console.log('selectedString = ' + selectedString);
18+
var typeAndValue = shared.getTypeAndValue(editor);
1919
}
20-
else selectedString = editor.document.getText(selection);
21-
22-
var reusableRegex = /{% *data ([^ %]*) *%}/;
23-
var regexmatchArray = selectedString.match(reusableRegex);
24-
25-
var regexmatchArrayResultPosition = 1;
20+
else { typeAndValue = shared.getTypeAndValue(editor.document.getText(selection)); }
21+
22+
// console.log('typeAndValue = ' + typeAndValue);
2623

27-
var matchType = null;
28-
if (regexmatchArray !== null) {
29-
matchType = 'reusable';
24+
if (!typeAndValue) {
25+
vscode.window.showInformationMessage("You didn't select a valid reusable, variable, or feature flag.");
26+
return;
3027
} else {
31-
var featureFlagRegex = /(?:{% *(?:if|ifversion) *([^ %]*) *%}|feature: '*([^ ']*)'* *$)/;
32-
regexmatchArray = selectedString.match(featureFlagRegex);
33-
// console.log('regexmatchArray = ' + regexmatchArray);
34-
if (regexmatchArray !== null) {
35-
matchType = 'feature';
36-
if (regexmatchArray[regexmatchArrayResultPosition] == null) { regexmatchArrayResultPosition = 2; }
37-
} else {
38-
vscode.window.showInformationMessage("You didn't select a valid reusable, variable, or feature flag.");
39-
return;
40-
}
28+
var matchType = typeAndValue[0];
29+
var pathOrName = typeAndValue[1];
4130
}
31+
4232
// console.log('matchType = ' + matchType);
4333

4434
var directorySeparator = "/";
@@ -49,11 +39,11 @@ function openMain() {
4939
// console.log('isWin = ' + isWin);
5040
// console.log('directorySeparator = ' + directorySeparator);
5141

52-
var filepath = regexmatchArray[regexmatchArrayResultPosition];
42+
var filepath = pathOrName;
5343
filepath = filepath.replace(/\./g, directorySeparator);
5444

5545
var regex = new RegExp(".*\\" + directorySeparator + "(docs|docs-internal)\\" + directorySeparator, "g");
56-
regexmatchArray = currentFilePath.match(regex);
46+
var regexmatchArray = currentFilePath.match(regex);
5747

5848

5949
switch (matchType) {
@@ -104,6 +94,8 @@ function openMain() {
10494
}, (err) => {
10595
vscode.window.showErrorMessage("File not found: " + filepath);
10696
});
97+
98+
10799
}
108100

109101
function findLineNumberOfVariable(variableName) {

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"displayName": "Open a reusable",
44
"description": "Open a variable or reusable referenced in the selected text",
55
"icon": "images/open-reusable-icon.png",
6-
"version": "1.5.2",
6+
"version": "1.6.0",
77
"publisher": "AlistairChristie",
88
"engines": {
99
"vscode": "^1.44.0"

shared.js

+93-7
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,106 @@ function testNoTab(editor) {
99
}
1010
}
1111

12-
function getReusableString(editor) {
13-
let reusableString = "";
14-
let objSelectTextAroundCursor = editor.document.getWordRangeAtPosition(editor.selection.active, /({%[^%]*%}|^ *feature: '*([^ ']*)'* *$)/);
15-
if (objSelectTextAroundCursor) {
16-
reusableString = editor.document.getText(objSelectTextAroundCursor);
12+
// This regex matches liquid tags. e.g:
13+
// - {%<something>%}
14+
const liquidTagRegex = /{%([^%])*%}/;
15+
16+
// This regex matches frontmatter feature flags definitions. e.g.:
17+
// - feature: '<something>'
18+
// - feature: <something>
19+
const frontmatterFeatureFlagRegex = /^ *feature: '*([^ ']*)'* *$/;
20+
21+
/* Gets the type and value of a reusable or feature flag.
22+
Returns an array with the type and value.
23+
24+
The input can either be a string that is the user selection, or an editor
25+
object that is the active editor with it's cursor position.
26+
27+
The return is an array with the type of docs thing being opened (i.e.
28+
reusable or feature), and the path or name of that thing. For reusables and
29+
variables, the path is the value of the liquid data tag (e.g.
30+
variables.product.prodname_dotcom), for feature flags it's the name of the
31+
flag (e.g. internal-actions).
32+
*/
33+
function getTypeAndValue(editorOrString) {
34+
let thingString = "";
35+
36+
// If the parameter is a string we can use that directly.
37+
// Otherwise, it's an editor and we have to get the string from the editor.
38+
if (typeof editorOrString === 'string') {
39+
//console.log('its a string');
40+
thingString = editorOrString;
41+
} else {
42+
//console.log('its an editor');
43+
thingString = getThingString(editorOrString);
1744
}
18-
return reusableString;
45+
46+
// console.log('thingString = ' + thingString);
47+
48+
// Check to see if it's a reusable or a feature flag in a liquid tag.
49+
if (liquidTagRegex.test(thingString)) {
50+
// This variable is all the contents inside the liquid tag.
51+
// e.g. For the liquid tag '{% data variables.product.prodname_dotcom %}', it is 'data variables.product.prodname_dotcom'
52+
let liquidTagContents = liquidTagRegex.exec(thingString)[0].trim();
53+
54+
//console.log('liquidTagContents = ' + liquidTagContents);
55+
56+
// If it's a reusable, return the type and value.
57+
// This regex checks for anything that matches:
58+
// - data <something>
59+
let reusableRegex = /data (\S*)/;
60+
if (reusableRegex.test(liquidTagContents)) {
61+
// This is just the path of the reusable, (i.e. the stuff in the liquid tag after 'data').
62+
let reusableValue = reusableRegex.exec(liquidTagContents)[1];
63+
return ['reusable', reusableValue];
64+
}
65+
66+
// If it's a feature flag in a liquid tag, return the type and value.
67+
// This regex checks for anything that matches:
68+
// - if <something>
69+
// - ifversion <something>
70+
let featureFlagRegex = /(?:if|ifversion) (\S*)/;
71+
if (featureFlagRegex.test(liquidTagContents)) {
72+
// This is just the name of the feature flag, (i.e. the stuff in the after 'if' or 'ifversion').
73+
let featureValue = featureFlagRegex.exec(liquidTagContents)[1];
74+
return ['feature', featureValue];
75+
}
76+
}
77+
78+
// check for frontmatter feature flags
79+
if (frontmatterFeatureFlagRegex.test(thingString)) {
80+
let featureValue = frontmatterFeatureFlagRegex.exec(thingString)[1];
81+
return ['feature', featureValue];
82+
}
83+
// If it's not a liquid or feature flag, return null.
84+
return null;
1985
}
2086

2187

88+
/* For an editor object that is the active editor with it's cursor position,
89+
get's the string of a reusable frontmatter feature flag.
90+
91+
The return is a string that is the liquid tag or frontmatter feature flag syntax.
92+
*/
93+
function getThingString(editor) {
94+
var regexArray = [liquidTagRegex, frontmatterFeatureFlagRegex];
95+
96+
// Go through the array of regexes and return a string that matches.
97+
for (var i = 0; i < regexArray.length; i++) {
98+
let objSelectTextAroundCursor = editor.document.getWordRangeAtPosition(editor.selection.active, regexArray[i]);
99+
if (objSelectTextAroundCursor) {
100+
return editor.document.getText(objSelectTextAroundCursor).trim();
101+
}
102+
}
103+
104+
// If we didn't find a match, return null.
105+
return null;
106+
}
22107

23108

24109

25110
module.exports = {
26111
testNoTab,
27-
getReusableString
112+
getTypeAndValue,
113+
getThingString
28114
};

0 commit comments

Comments
 (0)