diff --git a/src/services/cssHover.ts b/src/services/cssHover.ts index d24f73bf..c0945a6d 100644 --- a/src/services/cssHover.ts +++ b/src/services/cssHover.ts @@ -17,7 +17,10 @@ export class CSSHover { private readonly selectorPrinting: SelectorPrinting; private defaultSettings?: HoverSettings; - constructor(private readonly clientCapabilities: ClientCapabilities | undefined, private readonly cssDataManager: CSSDataManager) { + constructor( + private readonly clientCapabilities: ClientCapabilities | undefined, + private readonly cssDataManager: CSSDataManager, + ) { this.selectorPrinting = new SelectorPrinting(cssDataManager); } @@ -25,7 +28,6 @@ export class CSSHover { this.defaultSettings = settings; } - public doHover(document: TextDocument, position: Position, stylesheet: nodes.Stylesheet, settings = this.defaultSettings): Hover | null { function getRange(node: nodes.Node) { return Range.create(document.positionAt(node.offset), document.positionAt(node.end)); @@ -38,14 +40,24 @@ export class CSSHover { * Build up the hover by appending inner node's information */ let hover: Hover | null = null; + let flagOpts: { text: string; isMedia: boolean }; for (let i = 0; i < nodepath.length; i++) { const node = nodepath[i]; + if (node instanceof nodes.Media) { + const regex = /@media[^\{]+/g; + const matches = node.getText().match(regex); + flagOpts = { + isMedia: true, + text: matches?.[0]!, + }; + } + if (node instanceof nodes.Selector) { hover = { - contents: this.selectorPrinting.selectorToMarkedString(node), - range: getRange(node) + contents: this.selectorPrinting.selectorToMarkedString(node, flagOpts!), + range: getRange(node), }; break; } @@ -57,7 +69,7 @@ export class CSSHover { if (!startsWith(node.getText(), '@')) { hover = { contents: this.selectorPrinting.simpleSelectorToMarkedString(node), - range: getRange(node) + range: getRange(node), }; } break; @@ -71,7 +83,7 @@ export class CSSHover { if (contents) { hover = { contents, - range: getRange(node) + range: getRange(node), }; } else { hover = null; @@ -88,7 +100,7 @@ export class CSSHover { if (contents) { hover = { contents, - range: getRange(node) + range: getRange(node), }; } else { hover = null; @@ -99,16 +111,13 @@ export class CSSHover { if (node instanceof nodes.Node && node.type === nodes.NodeType.PseudoSelector) { const selectorName = node.getText(); - const entry = - selectorName.slice(0, 2) === '::' - ? this.cssDataManager.getPseudoElement(selectorName) - : this.cssDataManager.getPseudoClass(selectorName); + const entry = selectorName.slice(0, 2) === '::' ? this.cssDataManager.getPseudoElement(selectorName) : this.cssDataManager.getPseudoClass(selectorName); if (entry) { const contents = languageFacts.getEntryDescription(entry, this.doesSupportMarkdown(), settings); if (contents) { hover = { contents, - range: getRange(node) + range: getRange(node), }; } else { hover = null; @@ -118,7 +127,6 @@ export class CSSHover { } } - if (hover) { hover.contents = this.convertContents(hover.contents); } @@ -135,12 +143,12 @@ export class CSSHover { else if ('kind' in contents) { return { kind: 'plaintext', - value: contents.value + value: contents.value, }; } // MarkedString[] else if (Array.isArray(contents)) { - return contents.map(c => { + return contents.map((c) => { return typeof c === 'string' ? c : c.value; }); } diff --git a/src/services/selectorPrinting.ts b/src/services/selectorPrinting.ts index 23750200..6f96d0f6 100644 --- a/src/services/selectorPrinting.ts +++ b/src/services/selectorPrinting.ts @@ -12,10 +12,9 @@ import { CSSDataManager } from '../languageFacts/dataManager'; import { Parser } from '../parser/cssParser'; export class Element { - public parent: Element | null = null; public children: Element[] | null = null; - public attributes: { name: string, value: string; }[] | null = null; + public attributes: { name: string; value: string }[] | null = null; public findAttribute(name: string): string | null { if (this.attributes) { @@ -111,12 +110,9 @@ export class Element { } } -export class RootElement extends Element { - -} +export class RootElement extends Element { } export class LabelElement extends Element { - constructor(label: string) { super(); this.addAttr('name', label); @@ -124,14 +120,13 @@ export class LabelElement extends Element { } class MarkedStringPrinter { - private result: string[] = []; constructor(public quote: string) { // empty } - public print(element: Element): MarkedString[] { + public print(element: Element, flagOpts?: { isMedia: boolean; text: string }): MarkedString[] { this.result = []; if (element instanceof RootElement) { if (element.children) { @@ -140,8 +135,12 @@ class MarkedStringPrinter { } else { this.doPrint([element], 0); } - - const value = this.result.join('\n'); + let value; + if (flagOpts) { + value = `${flagOpts.text}\n … ` + this.result.join('\n'); + } else { + value = this.result.join('\n'); + } return [{ language: 'html', value }]; } @@ -198,9 +197,7 @@ class MarkedStringPrinter { } } - namespace quotes { - export function ensure(value: string, which: string): string { return which + remove(value) + which; } @@ -224,7 +221,6 @@ class Specificity { } export function toElement(node: nodes.SimpleSelector, parentElement?: Element | null): Element { - let result = new Element(); for (const child of node.getChildren()) { switch (child.type) { @@ -324,16 +320,13 @@ function unescape(content: string) { return content; } - export class SelectorPrinting { - constructor(private cssDataManager: CSSDataManager) { - - } + constructor(private cssDataManager: CSSDataManager) { } - public selectorToMarkedString(node: nodes.Selector): MarkedString[] { + public selectorToMarkedString(node: nodes.Selector, flagOpts?: { isMedia: boolean; text: string }): MarkedString[] { const root = selectorToElement(node); if (root) { - const markedStrings = new MarkedStringPrinter('"').print(root); + const markedStrings = new MarkedStringPrinter('"').print(root, flagOpts); markedStrings.push(this.selectorToSpecificityMarkedString(node)); return markedStrings; } else { @@ -355,7 +348,7 @@ export class SelectorPrinting { return false; } - return !!this.cssDataManager.getPseudoElement("::" + match[1]); + return !!this.cssDataManager.getPseudoElement('::' + match[1]); } private selectorToSpecificityMarkedString(node: nodes.Node): MarkedString { @@ -412,7 +405,7 @@ export class SelectorPrinting { case nodes.NodeType.ElementNameSelector: //ignore universal selector - if (element.matches("*")) { + if (element.matches('*')) { break; } @@ -438,7 +431,7 @@ export class SelectorPrinting { continue elementLoop; } - specificity.tag++; // pseudo element + specificity.tag++; // pseudo element continue elementLoop; } @@ -475,7 +468,7 @@ export class SelectorPrinting { // https://www.w3.org/TR/selectors-4/#the-nth-child-pseudo specificity.attr++; - // 23 = Binary Expression. + // 23 = Binary Expression. if (childElements.length === 3 && childElements[1].type === 23) { let mostSpecificListItem = calculateMostSpecificListItem(childElements[2].getChildren()); @@ -493,7 +486,7 @@ export class SelectorPrinting { const firstToken = parser.scanner.scan(); const secondToken = parser.scanner.scan(); - if (firstToken.text === 'n' || firstToken.text === '-n' && secondToken.text === 'of') { + if (firstToken.text === 'n' || (firstToken.text === '-n' && secondToken.text === 'of')) { const complexSelectorListNodes: nodes.Node[] = []; const complexSelectorText = pseudoSelectorText.slice(secondToken.offset + 2); const complexSelectorArray = complexSelectorText.split(','); @@ -516,7 +509,7 @@ export class SelectorPrinting { continue elementLoop; } - specificity.attr++; //pseudo class + specificity.attr++; //pseudo class continue elementLoop; } @@ -532,13 +525,11 @@ export class SelectorPrinting { }; const specificity = calculateScore(node); - return `[${l10n.t("Selector Specificity")}](https://developer.mozilla.org/docs/Web/CSS/Specificity): (${specificity.id}, ${specificity.attr}, ${specificity.tag})`; + return `[${l10n.t('Selector Specificity')}](https://developer.mozilla.org/docs/Web/CSS/Specificity): (${specificity.id}, ${specificity.attr}, ${specificity.tag})`; } - } class SelectorElementBuilder { - private prev: nodes.Node | null; private element: Element; @@ -564,7 +555,6 @@ class SelectorElementBuilder { } for (const selectorChild of selector.getChildren()) { - if (selectorChild instanceof nodes.SimpleSelector) { if (this.prev instanceof nodes.SimpleSelector) { const labelElement = new LabelElement('\u2026'); @@ -584,12 +574,13 @@ class SelectorElementBuilder { this.element.addChild(root); this.element = thisElement; } - if (selectorChild instanceof nodes.SimpleSelector || + if ( + selectorChild instanceof nodes.SimpleSelector || selectorChild.type === nodes.NodeType.SelectorCombinatorParent || selectorChild.type === nodes.NodeType.SelectorCombinatorShadowPiercingDescendant || selectorChild.type === nodes.NodeType.SelectorCombinatorSibling || - selectorChild.type === nodes.NodeType.SelectorCombinatorAllSiblings) { - + selectorChild.type === nodes.NodeType.SelectorCombinatorAllSiblings + ) { this.prev = selectorChild; } } diff --git a/src/test/css/hover.test.ts b/src/test/css/hover.test.ts index dbda9205..a8db8111 100644 --- a/src/test/css/hover.test.ts +++ b/src/test/css/hover.test.ts @@ -30,23 +30,31 @@ suite('CSS Hover', () => { contents: { kind: 'markdown', value: - "Sets the color of an element's text\n\n(Edge 12, Firefox 1, Safari 1, Chrome 1, IE 3, Opera 3)\n\nSyntax: <color>\n\n[MDN Reference](https://developer.mozilla.org/docs/Web/CSS/color)" - } + "Sets the color of an element's text\n\n(Edge 12, Firefox 1, Safari 1, Chrome 1, IE 3, Opera 3)\n\nSyntax: <color>\n\n[MDN Reference](https://developer.mozilla.org/docs/Web/CSS/color)", + }, }); - assertHover('.test { |color: blue; }', { - contents: { - kind: 'markdown', - value: - "[MDN Reference](https://developer.mozilla.org/docs/Web/CSS/color)" - } - }, undefined, { documentation: false }); - assertHover('.test { |color: blue; }', { - contents: { - kind: 'markdown', - value: - "Sets the color of an element's text\n\n(Edge 12, Firefox 1, Safari 1, Chrome 1, IE 3, Opera 3)\n\nSyntax: <color>" - } - }, undefined, { references: false }); + assertHover( + '.test { |color: blue; }', + { + contents: { + kind: 'markdown', + value: '[MDN Reference](https://developer.mozilla.org/docs/Web/CSS/color)', + }, + }, + undefined, + { documentation: false }, + ); + assertHover( + '.test { |color: blue; }', + { + contents: { + kind: 'markdown', + value: "Sets the color of an element's text\n\n(Edge 12, Firefox 1, Safari 1, Chrome 1, IE 3, Opera 3)\n\nSyntax: <color>", + }, + }, + undefined, + { references: false }, + ); /** * Reenable after converting specificity to use MarkupContent @@ -63,10 +71,7 @@ suite('CSS Hover', () => { test('specificity', () => { assertHover('.|foo {}', { - contents: [ - { language: 'html', value: '' }, - '[Selector Specificity](https://developer.mozilla.org/docs/Web/CSS/Specificity): (0, 1, 0)' - ] + contents: [{ language: 'html', value: '' }, '[Selector Specificity](https://developer.mozilla.org/docs/Web/CSS/Specificity): (0, 1, 0)'], }); }); }); @@ -75,13 +80,23 @@ suite('SCSS Hover', () => { test('nested', () => { assertHover( 'div { d|iv {} }', + { + contents: [{ language: 'html', value: '
\n …\n
' }, '[Selector Specificity](https://developer.mozilla.org/docs/Web/CSS/Specificity): (0, 0, 1)'], + }, + 'scss', + ); + assertHover( + '.foo{ .bar{ @media only screen{ .|bar{ } } } }', { contents: [ - { language: 'html', value: '
\n …\n
' }, - '[Selector Specificity](https://developer.mozilla.org/docs/Web/CSS/Specificity): (0, 0, 1)' - ] + { + language: 'html', + value: '@media only screen\n … \n …\n \n …\n ', + }, + '[Selector Specificity](https://developer.mozilla.org/docs/Web/CSS/Specificity): (0, 1, 0)', + ], }, - 'scss' + 'scss', ); }); @@ -89,9 +104,9 @@ suite('SCSS Hover', () => { assertHover( '.test { @|at-root { }', { - contents: [] + contents: [], }, - 'scss' + 'scss', ); }); });