From 4be731234e07d74c9b546a67ea57dd1303105f8c Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 25 Oct 2018 18:12:10 -0500 Subject: [PATCH 1/7] WIP: First attempt at showing specificity in a css selector hover. --- package.json | 1 + src/services/cssHover.ts | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index da11bb0f..0a5ba51c 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "xml2js": "^0.4.19" }, "dependencies": { + "specificity": "^0.4.1", "vscode-languageserver-types": "^3.13.0", "vscode-nls": "^4.0.0" }, diff --git a/src/services/cssHover.ts b/src/services/cssHover.ts index 047451af..cb0a7366 100644 --- a/src/services/cssHover.ts +++ b/src/services/cssHover.ts @@ -8,12 +8,19 @@ import * as nodes from '../parser/cssNodes'; import * as languageFacts from './languageFacts'; import { TextDocument, Range, Position, Hover, MarkedString } from 'vscode-languageserver-types'; import { selectorToMarkedString, simpleSelectorToMarkedString } from './selectorPrinting'; +import { calculate } from 'specificity'; export class CSSHover { constructor() { } + private calculateSpecificity(node: nodes.Node): MarkedString { + let specificity = calculate(node.getText()); + let value = "Specificity: " + specificity[0]["specificity"]; + return { language: 'text', value }; + } + public doHover(document: TextDocument, position: Position, stylesheet: nodes.Stylesheet): Hover { function getRange(node: nodes.Node) { @@ -26,8 +33,11 @@ export class CSSHover { for (let i = 0; i < nodepath.length; i++) { let node = nodepath[i]; if (node instanceof nodes.Selector) { + let content = selectorToMarkedString(node); + let specificity = this.calculateSpecificity(node); + content.push(specificity); return { - contents: selectorToMarkedString(node), + contents: content, range: getRange(node) }; } From 4c4f873df09a7810809f88a67f312d0d095eb648 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 25 Oct 2018 18:31:59 -0500 Subject: [PATCH 2/7] Move specificity code into selectorPrinting and remove the first score --- src/services/cssHover.ts | 12 +----------- src/services/selectorPrinting.ts | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/services/cssHover.ts b/src/services/cssHover.ts index cb0a7366..20187934 100644 --- a/src/services/cssHover.ts +++ b/src/services/cssHover.ts @@ -8,18 +8,11 @@ import * as nodes from '../parser/cssNodes'; import * as languageFacts from './languageFacts'; import { TextDocument, Range, Position, Hover, MarkedString } from 'vscode-languageserver-types'; import { selectorToMarkedString, simpleSelectorToMarkedString } from './selectorPrinting'; -import { calculate } from 'specificity'; export class CSSHover { constructor() { } - - private calculateSpecificity(node: nodes.Node): MarkedString { - let specificity = calculate(node.getText()); - let value = "Specificity: " + specificity[0]["specificity"]; - return { language: 'text', value }; - } public doHover(document: TextDocument, position: Position, stylesheet: nodes.Stylesheet): Hover { @@ -33,11 +26,8 @@ export class CSSHover { for (let i = 0; i < nodepath.length; i++) { let node = nodepath[i]; if (node instanceof nodes.Selector) { - let content = selectorToMarkedString(node); - let specificity = this.calculateSpecificity(node); - content.push(specificity); return { - contents: content, + contents: selectorToMarkedString(node), range: getRange(node) }; } diff --git a/src/services/selectorPrinting.ts b/src/services/selectorPrinting.ts index de880c51..c7fba1d7 100644 --- a/src/services/selectorPrinting.ts +++ b/src/services/selectorPrinting.ts @@ -7,6 +7,7 @@ import * as nodes from '../parser/cssNodes'; import { MarkedString } from 'vscode-languageserver-types'; import { Scanner } from '../parser/cssScanner'; +import { calculate } from 'specificity'; export class Element { @@ -310,14 +311,25 @@ function unescape(content: string) { return content; } +function specificityMarkedString(node: nodes.Node): MarkedString { + let specificityCalculation = calculate(node.getText())[0]; + let specificity = specificityCalculation["specificityArray"].slice(1); //remove first score (inline css) + let value = "Specificity: " + specificity.join(","); + return { language: 'text', value }; +} + export function selectorToMarkedString(node: nodes.Selector): MarkedString[] { let root = selectorToElement(node); - return new MarkedStringPrinter('"').print(root); + let markedStrings = new MarkedStringPrinter('"').print(root); + markedStrings.push(specificityMarkedString(node)); + return markedStrings; } export function simpleSelectorToMarkedString(node: nodes.SimpleSelector): MarkedString[] { let element = toElement(node); - return new MarkedStringPrinter('"').print(element); + let markedStrings = new MarkedStringPrinter('"').print(element); + markedStrings.push(specificityMarkedString(node)); + return markedStrings; } class SelectorElementBuilder { From 9288b68f8a75c7d6f07313e24ab1c1b8fa49ecaf Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 27 Oct 2018 10:12:29 -0500 Subject: [PATCH 3/7] Implement custom function to calculate specificity --- package.json | 1 - src/services/selectorPrinting.ts | 50 +++++++++++++++++++++++++++----- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 0a5ba51c..da11bb0f 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ "xml2js": "^0.4.19" }, "dependencies": { - "specificity": "^0.4.1", "vscode-languageserver-types": "^3.13.0", "vscode-nls": "^4.0.0" }, diff --git a/src/services/selectorPrinting.ts b/src/services/selectorPrinting.ts index c7fba1d7..c63096ac 100644 --- a/src/services/selectorPrinting.ts +++ b/src/services/selectorPrinting.ts @@ -7,7 +7,6 @@ import * as nodes from '../parser/cssNodes'; import { MarkedString } from 'vscode-languageserver-types'; import { Scanner } from '../parser/cssScanner'; -import { calculate } from 'specificity'; export class Element { @@ -311,24 +310,59 @@ function unescape(content: string) { return content; } -function specificityMarkedString(node: nodes.Node): MarkedString { - let specificityCalculation = calculate(node.getText())[0]; - let specificity = specificityCalculation["specificityArray"].slice(1); //remove first score (inline css) - let value = "Specificity: " + specificity.join(","); - return { language: 'text', value }; +function selectorToSpecifityMarkedString(node: nodes.Node): MarkedString { + //https://www.w3.org/TR/selectors-3/#specificity + function calculateScore(node: nodes.Node) { + node.getChildren().forEach(element => { + switch(element.type) { + case nodes.NodeType.IdentifierSelector: + specificity[0] += 1; //a + break; + case nodes.NodeType.ClassSelector: + case nodes.NodeType.AttributeSelector: + specificity[1] += 1; //b + break; + case nodes.NodeType.ElementNameSelector: + //ignore universal selector + if (element.getText() === "*") { + break; + } + specificity[2] += 1; //c + break; + case nodes.NodeType.PseudoSelector: + if (element.getText().match(/^::/)) { + specificity[2] += 1; //c (speudo element) + } else { + //ignore psuedo class NOT + if (element.getText().match(/^:not/i)) { + break; + } + specificity[1] += 1; //b (speudo class) + } + break; + } + if (element.getChildren().length > 0) { + calculateScore(element); + } + }); + } + + let specificity = [0, 0, 0]; //a,b,c + calculateScore(node); + return { language: 'text', value: "Specificity: " + specificity.join(",") }; } export function selectorToMarkedString(node: nodes.Selector): MarkedString[] { let root = selectorToElement(node); let markedStrings = new MarkedStringPrinter('"').print(root); - markedStrings.push(specificityMarkedString(node)); + markedStrings.push(selectorToSpecifityMarkedString(node)); return markedStrings; } export function simpleSelectorToMarkedString(node: nodes.SimpleSelector): MarkedString[] { let element = toElement(node); let markedStrings = new MarkedStringPrinter('"').print(element); - markedStrings.push(specificityMarkedString(node)); + markedStrings.push(selectorToSpecifityMarkedString(node)); return markedStrings; } From c0ebd40ec5f363e9b80cae6503f6d5080ae0fd86 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 27 Oct 2018 10:22:00 -0500 Subject: [PATCH 4/7] Fix tests --- src/test/css/selectorPrinting.test.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/test/css/selectorPrinting.test.ts b/src/test/css/selectorPrinting.test.ts index feb60f38..8c8aa726 100644 --- a/src/test/css/selectorPrinting.test.ts +++ b/src/test/css/selectorPrinting.test.ts @@ -121,22 +121,21 @@ suite('CSS - MarkedStringPrinter selectors', () => { test('descendant selector', function () { let p = new Parser(); - parseSelectorToMarkedString(p, 'e1 e2 { }', 'e1', [{ language: 'html', value: '\n …\n ' }]); - parseSelectorToMarkedString(p, 'e1 .div { }', 'e1', [{ language: 'html', value: '\n …\n ' }]); + parseSelectorToMarkedString(p, 'e1 e2 { }', 'e1', [{ language: 'html', value: '\n …\n ' }, { language: 'text', value: 'Specificity: 0,0,2'}]); + parseSelectorToMarkedString(p, 'e1 .div { }', 'e1', [{ language: 'html', value: '\n …\n ' }, { language: 'text', value: 'Specificity: 0,1,1'}]); }); test('child selector', function () { let p = new Parser(); - parseSelectorToMarkedString(p, 'e1 > e2 { }', 'e2', [{ language: 'html', value: '\n ' }]); + parseSelectorToMarkedString(p, 'e1 > e2 { }', 'e2', [{ language: 'html', value: '\n ' }, { language: 'text', value: 'Specificity: 0,0,2'}]); }); test('group selector', function () { let p = new Parser(); - parseSelectorToMarkedString(p, 'e1, e2 { }', 'e1', [{ language: 'html', value: '' }]); - parseSelectorToMarkedString(p, 'e1, e2 { }', 'e2', [{ language: 'html', value: '' }]); + parseSelectorToMarkedString(p, 'e1, e2 { }', 'e1', [{ language: 'html', value: '' }, { language: 'text', value: 'Specificity: 0,0,1'}]); + parseSelectorToMarkedString(p, 'e1, e2 { }', 'e2', [{ language: 'html', value: '' }, { language: 'text', value: 'Specificity: 0,0,1'}]); }); test('sibling selector', function () { let p = new Parser(); - parseSelectorToMarkedString(p, 'e1 + e2 { }', 'e2', [{ language: 'html', value: '\n' }]); - parseSelectorToMarkedString(p, 'e1 ~ e2 { }', 'e2', [{ language: 'html', value: '\n\n⋮\n' }]); + parseSelectorToMarkedString(p, 'e1 + e2 { }', 'e2', [{ language: 'html', value: '\n' }, { language: 'text', value: 'Specificity: 0,0,2'}]); + parseSelectorToMarkedString(p, 'e1 ~ e2 { }', 'e2', [{ language: 'html', value: '\n\n⋮\n' }, { language: 'text', value: 'Specificity: 0,0,2'}]); }); - }); From 892b38944843197ad13332b2912ad72c13d56e7a Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 27 Oct 2018 10:54:20 -0500 Subject: [PATCH 5/7] Add tests for selector hover specificities --- src/test/css/selectorPrinting.test.ts | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/test/css/selectorPrinting.test.ts b/src/test/css/selectorPrinting.test.ts index 8c8aa726..bc9dfa3a 100644 --- a/src/test/css/selectorPrinting.test.ts +++ b/src/test/css/selectorPrinting.test.ts @@ -139,3 +139,38 @@ suite('CSS - MarkedStringPrinter selectors', () => { parseSelectorToMarkedString(p, 'e1 ~ e2 { }', 'e2', [{ language: 'html', value: '\n\n⋮\n' }, { language: 'text', value: 'Specificity: 0,0,2'}]); }); }); + +suite('CSS - MarkedStringPrinter selectors specificities', () => { + let p = new Parser(); + test('attribute selector', function() { + parseSelectorToMarkedString(p, 'h1 + *[rel=up]', 'h1', [{ language: 'html', value: '

\n' }, { language: 'text', value: 'Specificity: 0,1,1' }]); + }); + + test('class selector', function() { + parseSelectorToMarkedString(p, 'ul ol li.red', 'ul', [{ language: 'html', value: '
    \n …\n
      \n …\n
    1. ' }, { language: 'text', value: 'Specificity: 0,1,3' }]); + parseSelectorToMarkedString(p, 'li.red.level', 'li', [{ language: 'html', value: '
    2. ' }, { language: 'text', value: 'Specificity: 0,2,1' }]); + }); + + test('pseudo class selector', function() { + parseSelectorToMarkedString(p, 'p:focus', 'p', [{ language: 'html', value: '

      ' }, { language: 'text', value: 'Specificity: 0,1,1' }]); + }); + + test('element selector', function() { + parseSelectorToMarkedString(p, 'li', 'li', [{ language: 'html', value: '

    3. ' }, { language: 'text', value: 'Specificity: 0,0,1' }]); + parseSelectorToMarkedString(p, 'ul li', 'ul', [{ language: 'html', value: '
        \n …\n
      • ' }, { language: 'text', value: 'Specificity: 0,0,2' }]); + parseSelectorToMarkedString(p, 'ul ol+li', 'ul', [{ language: 'html', value: '
          \n …\n
            \n
          1. ' }, { language: 'text', value: 'Specificity: 0,0,3' }]); + }); + + test('pseudo element selector', function() { + parseSelectorToMarkedString(p, 'p::after', 'p', [{ language: 'html', value: '

            ' }, { language: 'text', value: 'Specificity: 0,0,2' }]); + }); + + test('identifier selector', function() { + parseSelectorToMarkedString(p, '#x34y', '#x34y', [{ language: 'html', value: '' }, { language: 'text', value: 'Specificity: 1,0,0' }]); + }); + + test('ignore universal and not selector', function() { + parseSelectorToMarkedString(p, '*', '*', [{ language: 'html', value: '' }, { language: 'text', value: 'Specificity: 0,0,0' }]); + parseSelectorToMarkedString(p, '#s12:not(foo)', '#s12', [{ language: 'html', value: '' }, { language: 'text', value: 'Specificity: 1,0,1' }]); + }); +}); From fe392dec24c98a3f229813fbd1a10753c207b3b9 Mon Sep 17 00:00:00 2001 From: Kumar Harsh Date: Sun, 28 Oct 2018 15:46:18 -0500 Subject: [PATCH 6/7] Fix typo Co-Authored-By: Ragnoroct --- src/services/selectorPrinting.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/selectorPrinting.ts b/src/services/selectorPrinting.ts index c63096ac..73cfa205 100644 --- a/src/services/selectorPrinting.ts +++ b/src/services/selectorPrinting.ts @@ -331,7 +331,7 @@ function selectorToSpecifityMarkedString(node: nodes.Node): MarkedString { break; case nodes.NodeType.PseudoSelector: if (element.getText().match(/^::/)) { - specificity[2] += 1; //c (speudo element) + specificity[2] += 1; //c (pseudo element) } else { //ignore psuedo class NOT if (element.getText().match(/^:not/i)) { From 488d76bbf7c88fa82d55ce292d1892377ac982df Mon Sep 17 00:00:00 2001 From: Kumar Harsh Date: Sun, 28 Oct 2018 15:46:39 -0500 Subject: [PATCH 7/7] Fix typo Co-Authored-By: Ragnoroct --- src/services/selectorPrinting.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/selectorPrinting.ts b/src/services/selectorPrinting.ts index 73cfa205..ba272775 100644 --- a/src/services/selectorPrinting.ts +++ b/src/services/selectorPrinting.ts @@ -337,7 +337,7 @@ function selectorToSpecifityMarkedString(node: nodes.Node): MarkedString { if (element.getText().match(/^:not/i)) { break; } - specificity[1] += 1; //b (speudo class) + specificity[1] += 1; //b (pseudo class) } break; }