Skip to content

Commit

Permalink
Merge pull request #127 from docs/lucascosti/refactor-and-whitespace
Browse files Browse the repository at this point in the history
Refactor regex code, add liquid whitespace control compatibility
  • Loading branch information
hubwriter authored Dec 2, 2022
2 parents cd4fced + 218ddea commit ab8d9fa
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 86 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Change log

## 1.6.0
2 December 2022

Major refactor of the code by @lucascosti to facilitate future changes to this extension.

This version:
- Adds the ability for the extension to open reusables/feature versioning tags that contain whitespace control.
- Consolidates various regular expressions to improve code maintainability.

## 1.5.2
14 September 2022

Expand Down
62 changes: 7 additions & 55 deletions copier.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,68 +6,20 @@ function copyMain() {
var editor = vscode.window.activeTextEditor;
shared.testNoTab(editor);

var reusableString = shared.getReusableString(editor);
var reusableString = shared.getThingString(editor);

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

// Write to clipboard
vscode.env.clipboard.writeText(reusableString);
/*
// Check what's in the clipboard now:
vscode.env.clipboard.readText().then((text)=>{
let clipboard_content = text;
console.log('clipboard_content = ' + clipboard_content);
});
*/

var startSelection, endSelection, moveLeftBy, moveRightBy;
startSelection = endSelection = editor.document.offsetAt(editor.selection.anchor);
// Get the position of the start of the reusable
// Do this by parsing each character from the cursor leftwards
// until reaching "{" (for variables/reusables), or quotes or
// a colon (:) for article feature flag versioning
for (let parseCharacter = ""; !parseCharacter.match(/{|'|"|:/); startSelection--) {
let startPosition = editor.document.positionAt(startSelection);
let stopPosition = editor.document.positionAt(startSelection+1);
let textRange = new vscode.Range(startPosition, stopPosition);
parseCharacter = editor.document.getText(textRange);
// console.log('startSelection = ' + startSelection + '; parseCharacter = ' + parseCharacter);
// If it's an article feature flag versioning, move rightwards one or two.
if (parseCharacter.match(/'|"/)) { startSelection++; }
else if (parseCharacter.match(/:/)) { startSelection = startSelection + 2; }
moveLeftBy = endSelection - startSelection;
// console.log('move left by = ' + moveLeftBy);
}
// Get the position of the end of the reusable by parsing forwards
// until reaching "}" (for variables/reusables), or quotes or
// a line end for article feature flag versioning
for (let parseCharacter = ""; !parseCharacter.match(/}|'|"|\n/); endSelection++) {
let startPosition = editor.document.positionAt(endSelection);
let stopPosition = editor.document.positionAt(endSelection+1);
let textRange = new vscode.Range(startPosition, stopPosition);
parseCharacter = editor.document.getText(textRange);
// console.log('endSelection = ' + endSelection + '; parseCharacter = ' + parseCharacter);
// If it's an article feature flag versioning, move leftwards one.
if (parseCharacter.match(/'|"|\n/)) { endSelection--; }
moveRightBy = endSelection - startSelection;
// console.log('move left by = ' + moveRightBy);
}

// Move the cursor to the start of the reusable
vscode.commands.executeCommand("cursorMove", {
to: "left",
by: "character",
value: moveLeftBy
})
// Move the cursor to the end of the reusable, selecting the text moved over
vscode.commands.executeCommand("cursorMove", {
to: "right",
by: "character",
value: moveRightBy,
select: true
})

// Get the range of the reusableString
let stringRegex = new RegExp(reusableString, 'g');
let reusableStringRange = editor.document.getWordRangeAtPosition(editor.selection.active, stringRegex);

// set the selection to range of the reusableString
editor.selection = new vscode.Selection(reusableStringRange.start, reusableStringRange.end);
}
else {
vscode.window.showInformationMessage("Cursor is not within a reusable, variable, or feature flag.");
Expand Down
Binary file added open-reusables-1.6.0.vsix
Binary file not shown.
38 changes: 15 additions & 23 deletions opener.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,23 @@ function openMain() {
return;
}

// 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)
var selection = editor.selection;
if (selection.isEmpty) {
var selectedString = shared.getReusableString(editor);
// console.log('selectedString = ' + selectedString);
var typeAndValue = shared.getTypeAndValue(editor);
}
else selectedString = editor.document.getText(selection);

var reusableRegex = /{% *data ([^ %]*) *%}/;
var regexmatchArray = selectedString.match(reusableRegex);

var regexmatchArrayResultPosition = 1;
else { typeAndValue = shared.getTypeAndValue(editor.document.getText(selection)); }

// console.log('typeAndValue = ' + typeAndValue);

var matchType = null;
if (regexmatchArray !== null) {
matchType = 'reusable';
if (!typeAndValue) {
vscode.window.showInformationMessage("You didn't select a valid reusable, variable, or feature flag.");
return;
} else {
var featureFlagRegex = /(?:{% *(?:if|ifversion) *([^ %]*) *%}|feature: '*([^ ']*)'* *$)/;
regexmatchArray = selectedString.match(featureFlagRegex);
// console.log('regexmatchArray = ' + regexmatchArray);
if (regexmatchArray !== null) {
matchType = 'feature';
if (regexmatchArray[regexmatchArrayResultPosition] == null) { regexmatchArrayResultPosition = 2; }
} else {
vscode.window.showInformationMessage("You didn't select a valid reusable, variable, or feature flag.");
return;
}
var matchType = typeAndValue[0];
var pathOrName = typeAndValue[1];
}

// console.log('matchType = ' + matchType);

var directorySeparator = "/";
Expand All @@ -49,11 +39,11 @@ function openMain() {
// console.log('isWin = ' + isWin);
// console.log('directorySeparator = ' + directorySeparator);

var filepath = regexmatchArray[regexmatchArrayResultPosition];
var filepath = pathOrName;
filepath = filepath.replace(/\./g, directorySeparator);

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


switch (matchType) {
Expand Down Expand Up @@ -104,6 +94,8 @@ function openMain() {
}, (err) => {
vscode.window.showErrorMessage("File not found: " + filepath);
});


}

function findLineNumberOfVariable(variableName) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"displayName": "Open a reusable",
"description": "Open a variable or reusable referenced in the selected text",
"icon": "images/open-reusable-icon.png",
"version": "1.5.2",
"version": "1.6.0",
"publisher": "AlistairChristie",
"engines": {
"vscode": "^1.44.0"
Expand Down
100 changes: 93 additions & 7 deletions shared.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,106 @@ function testNoTab(editor) {
}
}

function getReusableString(editor) {
let reusableString = "";
let objSelectTextAroundCursor = editor.document.getWordRangeAtPosition(editor.selection.active, /({%[^%]*%}|^ *feature: '*([^ ']*)'* *$)/);
if (objSelectTextAroundCursor) {
reusableString = editor.document.getText(objSelectTextAroundCursor);
// This regex matches liquid tags. e.g:
// - {%<something>%}
const liquidTagRegex = /{%([^%])*%}/;

// This regex matches frontmatter feature flags definitions. e.g.:
// - feature: '<something>'
// - feature: <something>
const frontmatterFeatureFlagRegex = /^ *feature: '*([^ ']*)'* *$/;

/* Gets the type and value of a reusable or feature flag.
Returns an array with the type and value.
The input can either be a string that is the user selection, or an editor
object that is the active editor with it's cursor position.
The return is an array with the type of docs thing being opened (i.e.
reusable or feature), and the path or name of that thing. For reusables and
variables, the path is the value of the liquid data tag (e.g.
variables.product.prodname_dotcom), for feature flags it's the name of the
flag (e.g. internal-actions).
*/
function getTypeAndValue(editorOrString) {
let thingString = "";

// If the parameter is a string we can use that directly.
// Otherwise, it's an editor and we have to get the string from the editor.
if (typeof editorOrString === 'string') {
//console.log('its a string');
thingString = editorOrString;
} else {
//console.log('its an editor');
thingString = getThingString(editorOrString);
}
return reusableString;

// console.log('thingString = ' + thingString);

// Check to see if it's a reusable or a feature flag in a liquid tag.
if (liquidTagRegex.test(thingString)) {
// This variable is all the contents inside the liquid tag.
// e.g. For the liquid tag '{% data variables.product.prodname_dotcom %}', it is 'data variables.product.prodname_dotcom'
let liquidTagContents = liquidTagRegex.exec(thingString)[0].trim();

//console.log('liquidTagContents = ' + liquidTagContents);

// If it's a reusable, return the type and value.
// This regex checks for anything that matches:
// - data <something>
let reusableRegex = /data (\S*)/;
if (reusableRegex.test(liquidTagContents)) {
// This is just the path of the reusable, (i.e. the stuff in the liquid tag after 'data').
let reusableValue = reusableRegex.exec(liquidTagContents)[1];
return ['reusable', reusableValue];
}

// If it's a feature flag in a liquid tag, return the type and value.
// This regex checks for anything that matches:
// - if <something>
// - ifversion <something>
let featureFlagRegex = /(?:if|ifversion) (\S*)/;
if (featureFlagRegex.test(liquidTagContents)) {
// This is just the name of the feature flag, (i.e. the stuff in the after 'if' or 'ifversion').
let featureValue = featureFlagRegex.exec(liquidTagContents)[1];
return ['feature', featureValue];
}
}

// check for frontmatter feature flags
if (frontmatterFeatureFlagRegex.test(thingString)) {
let featureValue = frontmatterFeatureFlagRegex.exec(thingString)[1];
return ['feature', featureValue];
}
// If it's not a liquid or feature flag, return null.
return null;
}


/* For an editor object that is the active editor with it's cursor position,
get's the string of a reusable frontmatter feature flag.
The return is a string that is the liquid tag or frontmatter feature flag syntax.
*/
function getThingString(editor) {
var regexArray = [liquidTagRegex, frontmatterFeatureFlagRegex];

// Go through the array of regexes and return a string that matches.
for (var i = 0; i < regexArray.length; i++) {
let objSelectTextAroundCursor = editor.document.getWordRangeAtPosition(editor.selection.active, regexArray[i]);
if (objSelectTextAroundCursor) {
return editor.document.getText(objSelectTextAroundCursor).trim();
}
}

// If we didn't find a match, return null.
return null;
}



module.exports = {
testNoTab,
getReusableString
getTypeAndValue,
getThingString
};

0 comments on commit ab8d9fa

Please sign in to comment.