Skip to content

Commit 849f04e

Browse files
authored
Merge pull request #13 from gushuro/vendorprefix
Adding vendor prefixes to css abbreviations
2 parents d249a30 + 1521930 commit 849f04e

File tree

2 files changed

+90
-11
lines changed

2 files changed

+90
-11
lines changed

src/emmetHelper.ts

+31-11
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ let markupSnippetKeysRegex: RegExp[];
1717
const stylesheetCustomSnippetsKeyCache = new Map<string, string[]>();
1818
const htmlAbbreviationStartRegex = /^[a-z,A-Z,!,(,[,#,\.]/;
1919
const htmlAbbreviationEndRegex = /[a-z,A-Z,!,),\],#,\.,},\d,*,$]$/;
20-
const cssAbbreviationRegex = /^[a-z,A-Z,!,@,#]/;
20+
const cssAbbreviationRegex = /^-?[a-z,A-Z,!,@,#]/;
2121
const htmlAbbreviationRegex = /[a-z,A-Z]/;
2222
const emmetModes = ['html', 'pug', 'slim', 'haml', 'xml', 'xsl', 'jsx', 'css', 'scss', 'sass', 'less', 'stylus'];
2323
const commonlyUsedTags = ['div', 'span', 'p', 'b', 'i', 'body', 'html', 'ul', 'ol', 'li', 'head', 'section', 'canvas', 'dl', 'dt', 'dd', 'em', 'main', 'figure',
@@ -91,7 +91,7 @@ export function doComplete(document: TextDocument, position: Position, syntax: s
9191
// If abbreviation is valid, then expand it and ensure the expanded value is not noise
9292
if (isAbbreviationValid(syntax, abbreviation)) {
9393
try {
94-
expandedText = expand(abbreviation, expandOptions);
94+
expandedText = expandAbbreviationHelper(abbreviation, expandOptions);
9595
} catch (e) {
9696
}
9797

@@ -123,12 +123,13 @@ export function doComplete(document: TextDocument, position: Position, syntax: s
123123
const stylesheetCustomSnippetsKeys = stylesheetCustomSnippetsKeyCache.has(syntax) ? stylesheetCustomSnippetsKeyCache.get(syntax) : stylesheetCustomSnippetsKeyCache.get('css');
124124
completionItems = makeSnippetSuggestion(stylesheetCustomSnippetsKeys, currentWord, abbreviation, abbreviationRange, expandOptions, 'Emmet Custom Snippet', false);
125125

126-
if (!completionItems.find(x => x.textEdit.newText === expandedAbbr.textEdit.newText)) {
127-
126+
if (!completionItems.find(x => x.textEdit.newText === expandedAbbr.textEdit.newText)) {
127+
128128
// Fix for https://github.com/Microsoft/vscode/issues/28933#issuecomment-309236902
129129
// When user types in propertyname, emmet uses it to match with snippet names, resulting in width -> widows or font-family -> font: family
130130
// Filter out those cases here.
131-
const abbrRegex = new RegExp('.*' + abbreviation.split('').map(x => x === '$' ? '\\$' : x).join('.*') + '.*', 'i');
131+
let abbreviationWithoutDashPrefix = abbreviation[0] == '-' ? abbreviation.slice(1) : abbreviation;
132+
const abbrRegex = new RegExp('.*' + abbreviationWithoutDashPrefix.split('').map(x => x === '$' ? '\\$' : x).join('.*') + '.*', 'i');
132133
if (/\d/.test(abbreviation) || abbrRegex.test(expandedAbbr.label)) {
133134
completionItems.push(expandedAbbr);
134135
}
@@ -220,8 +221,7 @@ function addFinalTabStop(text): string {
220221
}
221222

222223
let maxTabStop = -1;
223-
let maxTabStopStart = -1;
224-
let maxTabStopEnd = -1;
224+
let maxTabStopRanges = [];
225225
let foundLastStop = false;
226226
let replaceWithLastStop = false;
227227
let i = 0;
@@ -270,17 +270,22 @@ function addFinalTabStop(text): string {
270270
// Decide to replace currentTabStop with ${0} only if its the max among all tabstops and is not a placeholder
271271
if (currentTabStop > maxTabStop) {
272272
maxTabStop = currentTabStop;
273-
maxTabStopStart = foundPlaceholder ? -1 : numberStart;
274-
maxTabStopEnd = foundPlaceholder ? -1 : numberEnd;
273+
maxTabStopRanges = [{ numberStart, numberEnd }];
275274
replaceWithLastStop = !foundPlaceholder;
275+
} else if (currentTabStop == maxTabStop) {
276+
maxTabStopRanges.push({numberStart, numberEnd});
276277
}
277278
}
278279
} catch (e) {
279280

280281
}
281282

282283
if (replaceWithLastStop && !foundLastStop) {
283-
text = text.substr(0, maxTabStopStart) + '0' + text.substr(maxTabStopEnd);
284+
for (let i = 0; i < maxTabStopRanges.length; i++) {
285+
let rangeStart = maxTabStopRanges[i].numberStart;
286+
let rangeEnd = maxTabStopRanges[i].numberEnd;
287+
text = text.substr(0, rangeStart) + '0' + text.substr(rangeEnd);
288+
}
284289
}
285290

286291
return text;
@@ -520,13 +525,28 @@ export function getExpandOptions(syntax: string, emmetConfig?: object, filter?:
520525
};
521526
}
522527

528+
function expandAbbreviationHelper(abbreviation: string, options: any) {
529+
let expandedText;
530+
let prefixes = ["-webkit-", "-moz-", "-ms-", "-o-"];
531+
abbreviation = abbreviation || "";
532+
if (isStyleSheet(options.syntax) && abbreviation[0] == '-') {
533+
let tmp = expand(abbreviation.substr(1), options);
534+
expandedText = tmp;
535+
for (let index = 0; index < prefixes.length; index++) {
536+
expandedText += "\n" + prefixes[index] + tmp;
537+
}
538+
} else {
539+
expandedText = expand(abbreviation, options);
540+
}
541+
return expandedText;
542+
}
523543
/**
524544
* Expands given abbreviation using given options
525545
* @param abbreviation string
526546
* @param options
527547
*/
528548
export function expandAbbreviation(abbreviation: string, options: any) {
529-
let expandedText = expand(abbreviation, options);
549+
let expandedText = expandAbbreviationHelper(abbreviation, options);
530550
return escapeNonTabStopDollar(addFinalTabStop(expandedText));
531551
}
532552

src/test/emmetHelperTest.ts

+59
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,66 @@ describe('Test completions', () => {
742742
assert.equal(matches[1].startsWith('Lorem'), true);
743743

744744
return Promise.resolve();
745+
});
746+
});
747+
748+
it('should provide completions using vendor prefixes', () => {
749+
return updateExtensionsPath(extensionsPath).then(() => {
750+
const testCases: [string, number, number, string, string, string][] = [
751+
752+
['brs', 0, 3, 'border-radius: ;', 'border-radius: |;', 'brs'],
753+
['brs5', 0, 4, 'border-radius: 5px;', 'border-radius: 5px;', 'brs5'],
754+
['-brs', 0, 4, 'border-radius: ;\n-webkit-border-radius: ;\n-moz-border-radius: ;\n-ms-border-radius: ;\n-o-border-radius: ;',
755+
'border-radius: |;\n-webkit-border-radius: |;\n-moz-border-radius: |;\n-ms-border-radius: |;\n-o-border-radius: |;', '-brs'],
756+
['-brs10', 0, 6, 'border-radius: 10px;\n-webkit-border-radius: 10px;\n-moz-border-radius: 10px;\n-ms-border-radius: 10px;\n-o-border-radius: 10px;',
757+
'border-radius: 10px;\n-webkit-border-radius: 10px;\n-moz-border-radius: 10px;\n-ms-border-radius: 10px;\n-o-border-radius: 10px;', '-brs10'],
758+
['-bdts', 0, 5, 'border-top-style: ;\n-webkit-border-top-style: ;\n-moz-border-top-style: ;\n-ms-border-top-style: ;\n-o-border-top-style: ;',
759+
'border-top-style: |;\n-webkit-border-top-style: |;\n-moz-border-top-style: |;\n-ms-border-top-style: |;\n-o-border-top-style: |;', '-bdts'],
760+
['-p', 0, 2, 'padding: ;\n-webkit-padding: ;\n-moz-padding: ;\n-ms-padding: ;\n-o-padding: ;',
761+
'padding: |;\n-webkit-padding: |;\n-moz-padding: |;\n-ms-padding: |;\n-o-padding: |;', '-p'],
762+
['-p10', 0, 4, 'padding: 10px;\n-webkit-padding: 10px;\n-moz-padding: 10px;\n-ms-padding: 10px;\n-o-padding: 10px;',
763+
'padding: 10px;\n-webkit-padding: 10px;\n-moz-padding: 10px;\n-ms-padding: 10px;\n-o-padding: 10px;', '-p10'],
764+
['-p10-20', 0, 7, 'padding: 10px 20px;\n-webkit-padding: 10px 20px;\n-moz-padding: 10px 20px;\n-ms-padding: 10px 20px;\n-o-padding: 10px 20px;',
765+
'padding: 10px 20px;\n-webkit-padding: 10px 20px;\n-moz-padding: 10px 20px;\n-ms-padding: 10px 20px;\n-o-padding: 10px 20px;', '-p10-20'],
766+
];
767+
768+
testCases.forEach(([content, positionLine, positionChar, expectedAbbr, expectedExpansion, expectedFilterText]) => {
769+
const document = TextDocument.create('test://test/test.css', 'css', 0, content);
770+
const position = Position.create(positionLine, positionChar);
771+
const completionList = doComplete(document, position, 'css', {
772+
preferences: {},
773+
showExpandedAbbreviation: 'always',
774+
showAbbreviationSuggestions: false,
775+
syntaxProfiles: {},
776+
variables: {}
777+
});
745778

779+
assert.equal(completionList.items[0].label, expectedAbbr);
780+
assert.equal(completionList.items[0].documentation, expectedExpansion);
781+
assert.equal(completionList.items[0].filterText, expectedFilterText);
782+
});
783+
return Promise.resolve();
784+
});
785+
});
786+
787+
788+
it('should expand with multiple vendor prefixes', () => {
789+
return updateExtensionsPath(null).then(() => {
790+
assert.equal(expandAbbreviation('brs', getExpandOptions('css', {})), 'border-radius: ${0};');
791+
assert.equal(expandAbbreviation('brs5', getExpandOptions('css', {})), 'border-radius: 5px;');
792+
assert.equal(expandAbbreviation('brs10px', getExpandOptions('css', {})), 'border-radius: 10px;');
793+
assert.equal(expandAbbreviation('-brs', getExpandOptions('css', {})), 'border-radius: ${0};\n-webkit-border-radius: ${0};\n-moz-border-radius: ${0};\n-ms-border-radius: ${0};\n-o-border-radius: ${0};');
794+
assert.equal(expandAbbreviation('-brs10', getExpandOptions('css', {})), 'border-radius: 10px;\n-webkit-border-radius: 10px;\n-moz-border-radius: 10px;\n-ms-border-radius: 10px;\n-o-border-radius: 10px;');
795+
assert.equal(expandAbbreviation('-bdts', getExpandOptions('css', {})), 'border-top-style: ${0};\n-webkit-border-top-style: ${0};\n-moz-border-top-style: ${0};\n-ms-border-top-style: ${0};\n-o-border-top-style: ${0};');
796+
assert.equal(expandAbbreviation('-bdts2px', getExpandOptions('css', {})), 'border-top-style: 2px;\n-webkit-border-top-style: 2px;\n-moz-border-top-style: 2px;\n-ms-border-top-style: 2px;\n-o-border-top-style: 2px;');
797+
assert.equal(expandAbbreviation('-p10-20', getExpandOptions('css', {})), 'padding: 10px 20px;\n-webkit-padding: 10px 20px;\n-moz-padding: 10px 20px;\n-ms-padding: 10px 20px;\n-o-padding: 10px 20px;');
798+
assert.equal(expandAbbreviation('-p10p20', getExpandOptions('css', {})), 'padding: 10% 20px;\n-webkit-padding: 10% 20px;\n-moz-padding: 10% 20px;\n-ms-padding: 10% 20px;\n-o-padding: 10% 20px;');
799+
800+
801+
//assert.equal(expandAbbreviation('-bgp', getExpandOptions('css', {})), 'background-position:${1:0} ${2:0};\n-webkit-background-position:${1:0} ${2:0};\n-moz-background-position:${1:0} ${2:0};\n-ms-background-position:${1:0} ${2:0};\n-o-background-position:${1:0} ${2:0};');
802+
//assert.equal(expandAbbreviation('bdr', getExpandOptions('css', {})), 'border-right: ${2:1px} ${3:solid} ${4:#000};');
803+
804+
return Promise.resolve();
746805
});
747806
});
748807

0 commit comments

Comments
 (0)