@@ -13,166 +13,164 @@ import { isDefined } from '../utils/objects';
1313import { CSSDataManager } from '../languageFacts/dataManager' ;
1414
1515export class CSSHover {
16- private supportsMarkdown : boolean | undefined ;
17- private readonly selectorPrinting : SelectorPrinting ;
18- private defaultSettings ?: HoverSettings ;
19-
20- constructor ( private readonly clientCapabilities : ClientCapabilities | undefined , private readonly cssDataManager : CSSDataManager ) {
21- this . selectorPrinting = new SelectorPrinting ( cssDataManager ) ;
22- }
23-
24- public configure ( settings : HoverSettings | undefined ) {
25- this . defaultSettings = settings ;
26- }
27-
28-
29- public doHover ( document : TextDocument , position : Position , stylesheet : nodes . Stylesheet , settings = this . defaultSettings ) : Hover | null {
30- function getRange ( node : nodes . Node ) {
31- return Range . create ( document . positionAt ( node . offset ) , document . positionAt ( node . end ) ) ;
32- }
33- const offset = document . offsetAt ( position ) ;
34- const nodepath = nodes . getNodePath ( stylesheet , offset ) ;
35-
36- /**
37- * nodepath is top-down
38- * Build up the hover by appending inner node's information
39- */
40- let hover : Hover | null = null ;
41- let flagOpts :{ text :string ; isMedia :boolean } ;
42-
43- for ( let i = 0 ; i < nodepath . length ; i ++ ) {
44- const node = nodepath [ i ] ;
45-
46- if ( node instanceof nodes . Media ) {
47- const regex = / @ m e d i a [ ^ \{ ] + / g;
48- const matches = node . getText ( ) . match ( regex ) ;
49- flagOpts = {
50- isMedia :true ,
51- text :matches ?. [ 0 ] . toString ( ) !
52- } ;
53- }
54-
55- if ( node instanceof nodes . Selector ) {
56- hover = {
57- contents : this . selectorPrinting . selectorToMarkedString ( < nodes . Selector > node , flagOpts ! ) ,
58- range : getRange ( node )
59- } ;
60- break ;
61- }
62-
63- if ( node instanceof nodes . SimpleSelector ) {
64- /**
65- * Some sass specific at rules such as `@at-root` are parsed as `SimpleSelector`
66- */
67- if ( ! startsWith ( node . getText ( ) , '@' ) ) {
68- hover = {
69- contents : this . selectorPrinting . simpleSelectorToMarkedString ( < nodes . SimpleSelector > node ) ,
70- range : getRange ( node )
71- } ;
72- }
73- break ;
74- }
75-
76- if ( node instanceof nodes . Declaration ) {
77- const propertyName = node . getFullPropertyName ( ) ;
78- const entry = this . cssDataManager . getProperty ( propertyName ) ;
79- if ( entry ) {
80- const contents = languageFacts . getEntryDescription ( entry , this . doesSupportMarkdown ( ) , settings ) ;
81- if ( contents ) {
82- hover = {
83- contents,
84- range : getRange ( node )
85- } ;
86- } else {
87- hover = null ;
88- }
89- }
90- continue ;
91- }
92-
93- if ( node instanceof nodes . UnknownAtRule ) {
94- const atRuleName = node . getText ( ) ;
95- const entry = this . cssDataManager . getAtDirective ( atRuleName ) ;
96- if ( entry ) {
97- const contents = languageFacts . getEntryDescription ( entry , this . doesSupportMarkdown ( ) , settings ) ;
98- if ( contents ) {
99- hover = {
100- contents,
101- range : getRange ( node )
102- } ;
103- } else {
104- hover = null ;
105- }
106- }
107- continue ;
108- }
109-
110- if ( node instanceof nodes . Node && node . type === nodes . NodeType . PseudoSelector ) {
111- const selectorName = node . getText ( ) ;
112- const entry =
113- selectorName . slice ( 0 , 2 ) === '::'
114- ? this . cssDataManager . getPseudoElement ( selectorName )
115- : this . cssDataManager . getPseudoClass ( selectorName ) ;
116- if ( entry ) {
117- const contents = languageFacts . getEntryDescription ( entry , this . doesSupportMarkdown ( ) , settings ) ;
118- if ( contents ) {
119- hover = {
120- contents,
121- range : getRange ( node )
122- } ;
123- } else {
124- hover = null ;
125- }
126- }
127- continue ;
128- }
129- }
130-
131-
132- if ( hover ) {
133- hover . contents = this . convertContents ( hover . contents ) ;
134- }
135-
136- return hover ;
137- }
138-
139- private convertContents ( contents : MarkupContent | MarkedString | MarkedString [ ] ) : MarkupContent | MarkedString | MarkedString [ ] {
140- if ( ! this . doesSupportMarkdown ( ) ) {
141- if ( typeof contents === 'string' ) {
142- return contents ;
143- }
144- // MarkupContent
145- else if ( 'kind' in contents ) {
146- return {
147- kind : 'plaintext' ,
148- value : contents . value
149- } ;
150- }
151- // MarkedString[]
152- else if ( Array . isArray ( contents ) ) {
153- return contents . map ( c => {
154- return typeof c === 'string' ? c : c . value ;
155- } ) ;
156- }
157- // MarkedString
158- else {
159- return contents . value ;
160- }
161- }
162-
163- return contents ;
164- }
165-
166- private doesSupportMarkdown ( ) {
167- if ( ! isDefined ( this . supportsMarkdown ) ) {
168- if ( ! isDefined ( this . clientCapabilities ) ) {
169- this . supportsMarkdown = true ;
170- return this . supportsMarkdown ;
171- }
172-
173- const hover = this . clientCapabilities . textDocument && this . clientCapabilities . textDocument . hover ;
174- this . supportsMarkdown = hover && hover . contentFormat && Array . isArray ( hover . contentFormat ) && hover . contentFormat . indexOf ( MarkupKind . Markdown ) !== - 1 ;
175- }
176- return < boolean > this . supportsMarkdown ;
177- }
16+ private supportsMarkdown : boolean | undefined ;
17+ private readonly selectorPrinting : SelectorPrinting ;
18+ private defaultSettings ?: HoverSettings ;
19+
20+ constructor (
21+ private readonly clientCapabilities : ClientCapabilities | undefined ,
22+ private readonly cssDataManager : CSSDataManager ,
23+ ) {
24+ this . selectorPrinting = new SelectorPrinting ( cssDataManager ) ;
25+ }
26+
27+ public configure ( settings : HoverSettings | undefined ) {
28+ this . defaultSettings = settings ;
29+ }
30+
31+ public doHover ( document : TextDocument , position : Position , stylesheet : nodes . Stylesheet , settings = this . defaultSettings ) : Hover | null {
32+ function getRange ( node : nodes . Node ) {
33+ return Range . create ( document . positionAt ( node . offset ) , document . positionAt ( node . end ) ) ;
34+ }
35+ const offset = document . offsetAt ( position ) ;
36+ const nodepath = nodes . getNodePath ( stylesheet , offset ) ;
37+
38+ /**
39+ * nodepath is top-down
40+ * Build up the hover by appending inner node's information
41+ */
42+ let hover : Hover | null = null ;
43+ let flagOpts : { text : string ; isMedia : boolean } ;
44+
45+ for ( let i = 0 ; i < nodepath . length ; i ++ ) {
46+ const node = nodepath [ i ] ;
47+
48+ if ( node instanceof nodes . Media ) {
49+ const regex = / @ m e d i a [ ^ \{ ] + / g;
50+ const matches = node . getText ( ) . match ( regex ) ;
51+ flagOpts = {
52+ isMedia : true ,
53+ text : matches ?. [ 0 ] ! ,
54+ } ;
55+ }
56+
57+ if ( node instanceof nodes . Selector ) {
58+ hover = {
59+ contents : this . selectorPrinting . selectorToMarkedString ( < nodes . Selector > node , flagOpts ! ) ,
60+ range : getRange ( node ) ,
61+ } ;
62+ break ;
63+ }
64+
65+ if ( node instanceof nodes . SimpleSelector ) {
66+ /**
67+ * Some sass specific at rules such as `@at-root` are parsed as `SimpleSelector`
68+ */
69+ if ( ! startsWith ( node . getText ( ) , '@' ) ) {
70+ hover = {
71+ contents : this . selectorPrinting . simpleSelectorToMarkedString ( < nodes . SimpleSelector > node ) ,
72+ range : getRange ( node ) ,
73+ } ;
74+ }
75+ break ;
76+ }
77+
78+ if ( node instanceof nodes . Declaration ) {
79+ const propertyName = node . getFullPropertyName ( ) ;
80+ const entry = this . cssDataManager . getProperty ( propertyName ) ;
81+ if ( entry ) {
82+ const contents = languageFacts . getEntryDescription ( entry , this . doesSupportMarkdown ( ) , settings ) ;
83+ if ( contents ) {
84+ hover = {
85+ contents,
86+ range : getRange ( node ) ,
87+ } ;
88+ } else {
89+ hover = null ;
90+ }
91+ }
92+ continue ;
93+ }
94+
95+ if ( node instanceof nodes . UnknownAtRule ) {
96+ const atRuleName = node . getText ( ) ;
97+ const entry = this . cssDataManager . getAtDirective ( atRuleName ) ;
98+ if ( entry ) {
99+ const contents = languageFacts . getEntryDescription ( entry , this . doesSupportMarkdown ( ) , settings ) ;
100+ if ( contents ) {
101+ hover = {
102+ contents,
103+ range : getRange ( node ) ,
104+ } ;
105+ } else {
106+ hover = null ;
107+ }
108+ }
109+ continue ;
110+ }
111+
112+ if ( node instanceof nodes . Node && node . type === nodes . NodeType . PseudoSelector ) {
113+ const selectorName = node . getText ( ) ;
114+ const entry = selectorName . slice ( 0 , 2 ) === '::' ? this . cssDataManager . getPseudoElement ( selectorName ) : this . cssDataManager . getPseudoClass ( selectorName ) ;
115+ if ( entry ) {
116+ const contents = languageFacts . getEntryDescription ( entry , this . doesSupportMarkdown ( ) , settings ) ;
117+ if ( contents ) {
118+ hover = {
119+ contents,
120+ range : getRange ( node ) ,
121+ } ;
122+ } else {
123+ hover = null ;
124+ }
125+ }
126+ continue ;
127+ }
128+ }
129+
130+ if ( hover ) {
131+ hover . contents = this . convertContents ( hover . contents ) ;
132+ }
133+
134+ return hover ;
135+ }
136+
137+ private convertContents ( contents : MarkupContent | MarkedString | MarkedString [ ] ) : MarkupContent | MarkedString | MarkedString [ ] {
138+ if ( ! this . doesSupportMarkdown ( ) ) {
139+ if ( typeof contents === 'string' ) {
140+ return contents ;
141+ }
142+ // MarkupContent
143+ else if ( 'kind' in contents ) {
144+ return {
145+ kind : 'plaintext' ,
146+ value : contents . value ,
147+ } ;
148+ }
149+ // MarkedString[]
150+ else if ( Array . isArray ( contents ) ) {
151+ return contents . map ( ( c ) => {
152+ return typeof c === 'string' ? c : c . value ;
153+ } ) ;
154+ }
155+ // MarkedString
156+ else {
157+ return contents . value ;
158+ }
159+ }
160+
161+ return contents ;
162+ }
163+
164+ private doesSupportMarkdown ( ) {
165+ if ( ! isDefined ( this . supportsMarkdown ) ) {
166+ if ( ! isDefined ( this . clientCapabilities ) ) {
167+ this . supportsMarkdown = true ;
168+ return this . supportsMarkdown ;
169+ }
170+
171+ const hover = this . clientCapabilities . textDocument && this . clientCapabilities . textDocument . hover ;
172+ this . supportsMarkdown = hover && hover . contentFormat && Array . isArray ( hover . contentFormat ) && hover . contentFormat . indexOf ( MarkupKind . Markdown ) !== - 1 ;
173+ }
174+ return < boolean > this . supportsMarkdown ;
175+ }
178176}
0 commit comments