10 |
11 | Permission is hereby granted, free of charge, to any person obtaining a copy of
12 | this software and associated documentation files (the "Software"), to deal in
13 | the Software without restriction, including without limitation the rights to
14 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
15 | the Software, and to permit persons to whom the Software is furnished to do so,
16 | subject to the following conditions:
17 |
18 | The above copyright notice and this permission notice shall be included in all
19 | copies or substantial portions of the Software.
20 |
21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
23 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
24 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
25 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 |
--------------------------------------------------------------------------------
/lib/PropertyProvider.coffee:
--------------------------------------------------------------------------------
1 | Utility = require './Utility'
2 | AbstractProvider = require './AbstractProvider'
3 |
4 | module.exports =
5 |
6 | ##*
7 | # Provides tooltips for member properties.
8 | ##
9 | class PropertyProvider extends AbstractProvider
10 | ###*
11 | * @inheritdoc
12 | ###
13 | hoverEventSelectors: '.syntax--property'
14 |
15 | ###*
16 | * @inheritdoc
17 | ###
18 | getTooltipForWord: (editor, bufferPosition, name) ->
19 | return new Promise (resolve, reject) =>
20 | failureHandler = () =>
21 | reject()
22 |
23 | resultingTypeSuccessHandler = (types) =>
24 | if types.length == 0
25 | reject()
26 | return
27 |
28 | successHandler = (classInfoArray) =>
29 | for classInfo in classInfoArray
30 | if name of classInfo.properties
31 | tooltipText = Utility.buildTooltipForProperty(classInfo.properties[name])
32 |
33 | resolve(tooltipText)
34 | return
35 |
36 | reject()
37 |
38 | promises = []
39 |
40 | for type in types
41 | promises.push @service.getClassInfo(type)
42 |
43 | Promise.all(promises).then(successHandler, failureHandler)
44 |
45 | return @service.getResultingTypesAt(editor, bufferPosition, true).then(
46 | resultingTypeSuccessHandler,
47 | failureHandler
48 | )
49 |
--------------------------------------------------------------------------------
/lib/ClassConstantProvider.coffee:
--------------------------------------------------------------------------------
1 | Utility = require './Utility'
2 | AbstractProvider = require './AbstractProvider'
3 |
4 | module.exports =
5 |
6 | ##*
7 | # Provides tooltips for member constants.
8 | ##
9 | class ClassConstantProvider extends AbstractProvider
10 | ###*
11 | * @inheritdoc
12 | ###
13 | hoverEventSelectors: '.syntax--constant.syntax--other.syntax--class'
14 |
15 | ###*
16 | * @inheritdoc
17 | ###
18 | getTooltipForWord: (editor, bufferPosition, name) ->
19 | return new Promise (resolve, reject) =>
20 | failureHandler = () =>
21 | reject()
22 |
23 | resultingTypeSuccessHandler = (types) =>
24 | if types.length == 0
25 | reject()
26 | return
27 |
28 | successHandler = (classInfoArray) =>
29 | for classInfo in classInfoArray
30 | if name of classInfo.constants
31 | tooltipText = Utility.buildTooltipForConstant(classInfo.constants[name])
32 |
33 | resolve(tooltipText)
34 | return
35 |
36 | reject()
37 |
38 | promises = []
39 |
40 | for type in types
41 | promises.push @service.getClassInfo(type)
42 |
43 | Promise.all(promises).then(successHandler, failureHandler)
44 |
45 | return @service.getResultingTypesAt(editor, bufferPosition, true).then(
46 | resultingTypeSuccessHandler,
47 | failureHandler
48 | )
49 |
--------------------------------------------------------------------------------
/lib/MethodProvider.coffee:
--------------------------------------------------------------------------------
1 | Utility = require './Utility'
2 | AbstractProvider = require './AbstractProvider'
3 |
4 | module.exports =
5 |
6 | ##*
7 | # Provides tooltips for global constants.
8 | ##
9 | class ClassProvider extends AbstractProvider
10 | ###*
11 | * @inheritdoc
12 | ###
13 | hoverEventSelectors: '.syntax--function-call.syntax--object, .syntax--function-call.syntax--static'
14 |
15 | ###*
16 | * @inheritdoc
17 | ###
18 | getTooltipForWord: (editor, bufferPosition, name) ->
19 | return new Promise (resolve, reject) =>
20 | failureHandler = () =>
21 | reject()
22 |
23 | resultingTypeSuccessHandler = (types) =>
24 | if types.length == 0
25 | reject()
26 | return
27 |
28 | successHandler = (classInfoArray) =>
29 | for classInfo in classInfoArray
30 | if name of classInfo.methods
31 | tooltipText = Utility.buildTooltipForFunction(classInfo.methods[name])
32 |
33 | resolve(tooltipText)
34 | return
35 |
36 | reject()
37 |
38 | promises = []
39 |
40 | for type in types
41 | promises.push @service.getClassInfo(type)
42 |
43 | Promise.all(promises).then(successHandler, failureHandler)
44 |
45 | return @service.getResultingTypesAt(editor, bufferPosition, true).then(
46 | resultingTypeSuccessHandler,
47 | failureHandler
48 | )
49 |
--------------------------------------------------------------------------------
/lib/FunctionDefinitionProvider.coffee:
--------------------------------------------------------------------------------
1 | Utility = require './Utility'
2 | AbstractProvider = require './AbstractProvider'
3 |
4 | module.exports =
5 |
6 | ##*
7 | # Provides tooltips on function and method definitions.
8 | ##
9 | class FunctionDefinitionProvider extends AbstractProvider
10 | ###*
11 | * @inheritdoc
12 | ###
13 | hoverEventSelectors: '.syntax--entity.syntax--name.syntax--function, .syntax--support.syntax--function.syntax--magic'
14 |
15 | ###*
16 | * @inheritdoc
17 | ###
18 | getTooltipForWord: (editor, bufferPosition, name) ->
19 | return new Promise (resolve, reject) =>
20 | failureHandler = () =>
21 | reject()
22 |
23 | successHandler = (currentClassName) =>
24 | if currentClassName?
25 | successHandler = (classInfo) =>
26 | if name of classInfo.methods
27 | tooltipText = Utility.buildTooltipForFunction(classInfo.methods[name])
28 |
29 | resolve(tooltipText)
30 | return
31 |
32 | reject()
33 |
34 | return @service.getClassInfo(currentClassName).then(successHandler, failureHandler)
35 |
36 | else
37 | successHandler = (functions) =>
38 | if functions and name of functions
39 | resolve(Utility.buildTooltipForFunction(functions[name]))
40 | return
41 |
42 | reject()
43 |
44 | return @service.getGlobalFunctions().then(successHandler, failureHandler)
45 |
46 | return @service.determineCurrentClassName(editor, bufferPosition).then(successHandler, failureHandler)
47 |
--------------------------------------------------------------------------------
/lib/Main.coffee:
--------------------------------------------------------------------------------
1 | module.exports =
2 | ###*
3 | * List of tooltip providers.
4 | ###
5 | providers: []
6 |
7 | ###*
8 | * Activates the package.
9 | ###
10 | activate: ->
11 |
12 | ###*
13 | * Deactivates the package.
14 | ###
15 | deactivate: ->
16 | @deactivateProviders()
17 |
18 | ###*
19 | * Activates the providers using the specified service.
20 | ###
21 | activateProviders: (service) ->
22 | ClassProvider = require './ClassProvider'
23 | MethodProvider = require './MethodProvider'
24 | PropertyProvider = require './PropertyProvider'
25 | FunctionProvider = require './FunctionProvider'
26 | ConstantProvider = require './ConstantProvider'
27 | ClassConstantProvider = require './ClassConstantProvider'
28 | FunctionDefinitionProvider = require './FunctionDefinitionProvider'
29 |
30 | @providers = []
31 | @providers.push new ClassProvider()
32 | @providers.push new MethodProvider()
33 | @providers.push new FunctionProvider()
34 | @providers.push new PropertyProvider()
35 | @providers.push new FunctionDefinitionProvider()
36 |
37 | # The selector from the constant provider will still match class constants due to the way SubAtom does its
38 | # class selector checks. However, the reverse doesn't hold so if we add the class constant provider first,
39 | # we will not run into problems.
40 | @providers.push new ClassConstantProvider()
41 | @providers.push new ConstantProvider()
42 |
43 | for provider in @providers
44 | provider.activate(service)
45 |
46 | ###*
47 | * Deactivates any active providers.
48 | ###
49 | deactivateProviders: () ->
50 | for provider in @providers
51 | provider.deactivate()
52 |
53 | @providers = []
54 |
55 | ###*
56 | * Sets the php-integrator service.
57 | *
58 | * @param {mixed} service
59 | ###
60 | setService: (service) ->
61 | @activateProviders(service)
62 |
63 | {Disposable} = require 'atom'
64 |
65 | return new Disposable => @deactivateProviders()
66 |
--------------------------------------------------------------------------------
/lib/ConstantProvider.coffee:
--------------------------------------------------------------------------------
1 | Utility = require './Utility'
2 | AbstractProvider = require './AbstractProvider'
3 |
4 | module.exports =
5 |
6 | ##*
7 | # Provides tooltips for member methods.
8 | ##
9 | class MethodProvider extends AbstractProvider
10 | ###*
11 | * @inheritdoc
12 | ###
13 | hoverEventSelectors: '.syntax--constant.syntax--other.syntax--php, .syntax--support.syntax--other.syntax--namespace.syntax--php'
14 |
15 | ###*
16 | * @inheritdoc
17 | ###
18 | getTooltipForWord: (editor, bufferPosition, name) ->
19 | return new Promise (resolve, reject) =>
20 | failureHandler = () =>
21 | reject()
22 |
23 | resolveTypeHandler = (type) =>
24 | successHandler = (constants) =>
25 | if type?[0] != '\\'
26 | type = '\\' + type
27 |
28 | if constants and type of constants
29 | resolve(Utility.buildTooltipForConstant(constants[type]))
30 | return
31 |
32 | reject()
33 |
34 | return @service.getGlobalConstants().then(successHandler, failureHandler)
35 |
36 | return @service.resolveType(editor.getPath(), bufferPosition.row + 1, name, 'constant').then(
37 | resolveTypeHandler,
38 | failureHandler
39 | )
40 |
41 | ###*
42 | * @inheritdoc
43 | ###
44 | getSelectorFromEvent: (event) ->
45 | return @getClassSelectorFromEvent(event)
46 |
47 | ###*
48 | * Gets the correct selector for the constant that is part of the specified event.
49 | *
50 | * @param {jQuery.Event} event A jQuery event.
51 | *
52 | * @return {object|null} A selector to be used with jQuery.
53 | ###
54 | getClassSelectorFromEvent: (event) ->
55 | selector = event.currentTarget
56 |
57 | $ = require 'jquery'
58 |
59 | if $(selector).prev().hasClass('namespace') && $(selector).hasClass('constant')
60 | return $([$(selector).prev()[0], selector])
61 |
62 | if $(selector).next().hasClass('constant') && $(selector).hasClass('namespace')
63 | return $([selector, $(selector).next()[0]])
64 |
65 | return $(selector)
66 |
67 | ###*
68 | * @inheritdoc
69 | ###
70 | getPopoverElementFromSelector: (selector) ->
71 | $ = require 'jquery'
72 |
73 | # getSelectorFromEvent can return multiple items because namespaces and constants are different HTML elements.
74 | # We have to select one to attach the popover to.
75 | array = $(selector).toArray()
76 | return array[array.length - 1]
77 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 1.1.6
2 | * Rename package to demarcate legacy status.
3 |
4 | ## 1.1.5
5 | * Fix tooltips for use statements not always working in Atom >= 1.13.
6 |
7 | ## 1.1.4
8 | * Fix tooltips for FQCN's not working.
9 |
10 | ## 1.1.3
11 | * Fix tooltips no longer working due to updated syntax class selectors.
12 |
13 | ## 1.1.2
14 | * Fix deprecations.
15 |
16 | ## 1.1.1
17 | * Ensure the subAtom property is always initialized.
18 |
19 | ## 1.1.0 (base 2.0.0)
20 | * The information displayed has been tweaked.
21 | * Fix navigation to unqualified global constants not working.
22 | * Fix navigation to unqualified global functions not working.
23 | * Fix navigation to global constants imported using use statements not working.
24 | * Fix navigation to global functions imported using use statements not working.
25 | * Fix tooltips for qualified global constants with namespace prefix not working.
26 | * Fix tooltips for qualified global functions with namespace prefix not working.
27 |
28 | ## 1.0.4
29 | * Rename the package and repository.
30 |
31 | ## 1.0.3
32 | * Fix tooltips not working on built-in PHP functions - properly this time.
33 |
34 | ## 1.0.2
35 | * Fix tooltips not working on built-in PHP functions.
36 |
37 | ## 1.0.1
38 | * Fix the version specifier not being compatible with newer versions of the base service.
39 |
40 | ## 1.0.0 (base 1.0.0)
41 | * Tweak the styling of the return block a bit.
42 | * The return section of tooltips will always be shown, regardless of whether the type is known or not.
43 | * Unknown return types will now no longer be shown as 'mixed', which makes it clearer to the user that documentation is missing.
44 |
45 | ## 0.6.1 (base 0.9.0)
46 | * Updated to use the most recent version of the base service.
47 |
48 | ## 0.6.0 (base 0.8.0)
49 | * A couple of tooltip providers have become more asynchronous, improving responsiveness.
50 |
51 | ## 0.5.0 (base 0.7.0)
52 | * Tooltip fetching is now mostly asynchronous, which should result in less minor hickups and freezes.
53 | * Class names after the class keyword (to start the definition) will now also show a tooltip, much like function definitions.
54 |
55 | ## 0.4.2
56 | * Fixed the tooltip not showing when hovering over magic methods such as `__construct`.
57 |
58 | ## 0.4.1
59 | * Catch exceptions properly.
60 |
61 | ## 0.4.0 (base 0.6.0)
62 | * Minor bugfixes.
63 | * Tooltips will now show up when hovering over function and method definitions as well. This is useful when you are inheriting documentation, so you can view what is actually inherited.
64 |
65 | ## 0.3.1 (base 0.5.0)
66 | * Updated to work with the most recent service from the base package.
67 |
68 | ## 0.3.0 (base 0.4.0)
69 | * Added tooltips for class and global constants.
70 | * Fixed error `Cannot read property 'length' of undefined` being thrown sometimes when there was a property and a method with the same name.
71 |
72 | ## 0.2.1
73 | * Wait for the language-php package to become activated on startup.
74 |
75 | ## 0.2.0
76 | * Tooltips are now displayed for global functions. `[1]`
77 |
78 | `[1]` Note that the built-in PHP functions don't have documentation that can be retrieved via reflection, so this only applies to user defined functions at the moment.
79 |
80 | ## 0.1.0
81 | * Initial release.
82 |
--------------------------------------------------------------------------------
/lib/ClassProvider.coffee:
--------------------------------------------------------------------------------
1 | Utility = require './Utility'
2 | AbstractProvider = require './AbstractProvider'
3 |
4 | module.exports =
5 |
6 | ##*
7 | # Provides tooltips for classes, traits, interfaces, ...
8 | ##
9 | class ClassProvider extends AbstractProvider
10 | ###*
11 | * @inheritdoc
12 | ###
13 | hoverEventSelectors: '.syntax--entity.syntax--name.syntax--type.syntax--class, .syntax--entity.syntax--inherited-class, .syntax--support.syntax--namespace, .syntax--support.syntax--class'
14 |
15 | ###*
16 | * @inheritdoc
17 | ###
18 | getTooltipForWord: (editor, bufferPosition, name) ->
19 | return new Promise (resolve, reject) =>
20 | scopeChain = editor.scopeDescriptorForBufferPosition(bufferPosition).getScopeChain()
21 |
22 | failureHandler = () =>
23 | reject()
24 |
25 | successHandler = (currentClassName) =>
26 | successHandler = (classInfo) =>
27 | tooltipText = Utility.buildTooltipForClasslike(classInfo)
28 |
29 | resolve(tooltipText)
30 |
31 | firstPromise = null
32 |
33 | # Don't attempt to resolve class names in use statements. Note that scope descriptors for trait use
34 | # statements and actual "import" use statements are the same, so we have no choice but to use class
35 | # information for this: if we are inside a class, we can't be looking at a use statement.
36 | if scopeChain.indexOf('.support.other.namespace.use') == -1 or currentClassName?
37 | firstPromise = @service.resolveTypeAt(editor, bufferPosition, name, 'classlike')
38 |
39 | else
40 | firstPromise = new Promise (resolve, reject) ->
41 | resolve(name)
42 |
43 | firstPromiseHandler = (name) =>
44 | return @service.getClassInfo(name).then(successHandler, failureHandler)
45 |
46 | return firstPromise.then(firstPromiseHandler, failureHandler)
47 |
48 | return @service.determineCurrentClassName(editor, bufferPosition).then(successHandler, failureHandler)
49 |
50 | ###*
51 | * @inheritdoc
52 | ###
53 | getSelectorFromEvent: (event) ->
54 | return @getClassSelectorFromEvent(event)
55 |
56 | ###*
57 | * Gets the correct selector for the class or namespace that is part of the specified event.
58 | *
59 | * @param {jQuery.Event} event A jQuery event.
60 | *
61 | * @return {object|null} A selector to be used with jQuery.
62 | ###
63 | getClassSelectorFromEvent: (event) ->
64 | selector = event.currentTarget
65 |
66 | $ = require 'jquery'
67 |
68 | if $(selector).parent().hasClass('syntax--function syntax--argument')
69 | return $(selector).parent().children('.syntax--namespace, .syntax--class:not(.syntax--operator):not(.syntax--constant)')
70 |
71 | if $(selector).prev().hasClass('syntax--namespace') && $(selector).hasClass('syntax--class')
72 | return $([$(selector).prev()[0], selector])
73 |
74 | if $(selector).next().hasClass('syntax--class') && $(selector).hasClass('syntax--namespace')
75 | return $([selector, $(selector).next()[0]])
76 |
77 | if $(selector).prev().hasClass('syntax--namespace') || $(selector).next().hasClass('syntax--inherited-class')
78 | return $(selector).parent().children('.syntax--namespace, .syntax--inherited-class')
79 |
80 | if $(selector).next().hasClass('syntax--constant') && $(selector).hasClass('syntax--namespace')
81 | return null
82 |
83 | return selector
84 |
85 | ###*
86 | * @inheritdoc
87 | ###
88 | getPopoverElementFromSelector: (selector) ->
89 | $ = require 'jquery'
90 |
91 | # getSelectorFromEvent can return multiple items because namespaces and class names are different HTML elements.
92 | # We have to select one to attach the popover to.
93 | array = $(selector).toArray()
94 | return array[array.length - 1]
95 |
--------------------------------------------------------------------------------
/lib/Utility.coffee:
--------------------------------------------------------------------------------
1 | Utility = require './Utility'
2 |
3 | module.exports =
4 | ###*
5 | * Builds a tooltip for a classlike structural element.
6 | *
7 | * @param {Object} value
8 | *
9 | * @return {string}
10 | ###
11 | buildTooltipForClasslike: (value) ->
12 | description = ''
13 |
14 | # Show the summary (short description).
15 | description += ''
16 | description += (if value.shortDescription then value.shortDescription else '(No documentation available)')
17 | description += '
'
18 |
19 | # Show the (long) description.
20 | if value.longDescription?.length > 0
21 | description += ''
22 | description += "
Description
"
23 | description += "
" + value.longDescription + "
"
24 | description += "
"
25 |
26 | # Shwo the FQCN.
27 | description += ''
28 | description += "
Full Name
"
29 | description += "
" + value.name + "
"
30 | description += "
"
31 |
32 | # Show the type.
33 | type = ''
34 |
35 | if value.type == 'class'
36 | type = (if value.isAbstract then 'Abstract ' else '') + 'Class'
37 |
38 | else if value.type == 'trait'
39 | type = 'Trait'
40 |
41 | else if value.type == 'interface'
42 | type = 'Interface'
43 |
44 | description += ''
45 | description += "
Type
"
46 | description += "
" + type + "
"
47 | description += "
"
48 |
49 | return description
50 |
51 | ###*
52 | * Builds a tooltip for a function.
53 | *
54 | * @param {Object} value
55 | *
56 | * @return {string}
57 | ###
58 | buildTooltipForFunction: (value) ->
59 | description = ""
60 |
61 | # Show the summary (short description).
62 | description += ''
63 | description += (if value.shortDescription then value.shortDescription else '(No documentation available)')
64 | description += '
'
65 |
66 | # Show the (long) description.
67 | if value.longDescription?.length > 0
68 | description += ''
69 | description += "
Description
"
70 | description += "
" + value.longDescription + "
"
71 | description += "
"
72 |
73 | # Show the parameters the method has.
74 | parametersDescription = ""
75 |
76 | for param in value.parameters
77 | parametersDescription += ''
78 |
79 | parametersDescription += '| • '
80 |
81 | if param.isOptional
82 | parametersDescription += '['
83 |
84 | if param.isReference
85 | parametersDescription += '&'
86 |
87 | if param.isVariadic
88 | parametersDescription += '...'
89 |
90 | parametersDescription += '$' + param.name
91 |
92 | if param.isOptional
93 | parametersDescription += ']'
94 |
95 | parametersDescription += " | "
96 |
97 | parametersDescription += '' + (if param.types.length > 0 then @buildTypeSpecificationFromTypeArray(param.types) else ' ') + ' | '
98 | parametersDescription += '' + (if param.description then param.description else ' ') + ' | '
99 |
100 | parametersDescription += "
"
101 |
102 | if parametersDescription.length > 0
103 | description += ''
104 | description += "
Parameters
"
105 | description += "
" + parametersDescription + "
"
106 | description += "
"
107 |
108 | returnValue = @buildTypeSpecificationFromTypeArray(value.returnTypes)
109 |
110 | returnDescription = ''
111 |
112 | if value.returnDescription
113 | returnDescription = ' — ' + value.returnDescription
114 |
115 | description += ''
116 | description += '
Returns
'
117 | description += '
' + returnValue + '' + returnDescription + '
'
118 | description += "
"
119 |
120 | # Show an overview of the exceptions the method can throw.
121 | throwsDescription = ""
122 |
123 | for exceptionType,thrownWhenDescription of value.throws
124 | throwsDescription += ""
125 | throwsDescription += "• " + exceptionType + ""
126 |
127 | if thrownWhenDescription
128 | throwsDescription += ' ' + thrownWhenDescription
129 |
130 | throwsDescription += "
"
131 |
132 | if throwsDescription.length > 0
133 | description += ''
134 | description += "
Throws
"
135 | description += "
" + throwsDescription + "
"
136 | description += "
"
137 |
138 | return description
139 |
140 | ###*
141 | * Builds a tooltip for a property.
142 | *
143 | * @param {Object} value
144 | *
145 | * @return {string}
146 | ###
147 | buildTooltipForProperty: (value) ->
148 | # Create a useful description to show in the tooltip.
149 | description = ''
150 |
151 | # Show the summary (short description).
152 | description += ''
153 | description += (if value.shortDescription then value.shortDescription else '(No documentation available)')
154 | description += '
'
155 |
156 | # Show the (long) description.
157 | if value.longDescription?.length > 0
158 | description += ''
159 | description += "
Description
"
160 | description += "
" + value.longDescription + "
"
161 | description += "
"
162 |
163 | returnValue = @buildTypeSpecificationFromTypeArray(value.types)
164 |
165 | returnDescription = ''
166 |
167 | if value.returnDescription
168 | returnDescription = ' — ' + value.returnDescription
169 |
170 | description += ''
171 | description += '
Type
'
172 | description += '
' + returnValue + '' + returnDescription + '
'
173 | description += "
"
174 |
175 | ###*
176 | * Builds a tooltip for a constant.
177 | *
178 | * @param {Object} value
179 | *
180 | * @return {string}
181 | ###
182 | buildTooltipForConstant: (value) ->
183 | # Create a useful description to show in the tooltip.
184 | description = ''
185 |
186 | # Show the summary (short description).
187 | description += ''
188 | description += (if value.shortDescription then value.shortDescription else '(No documentation available)')
189 | description += '
'
190 |
191 | # Show the (long) description.
192 | if value.longDescription?.length > 0
193 | description += ''
194 | description += "
Description
"
195 | description += "
" + value.longDescription + "
"
196 | description += "
"
197 |
198 | returnValue = @buildTypeSpecificationFromTypeArray(value.types)
199 |
200 | returnDescription = ''
201 |
202 | if value.returnDescription
203 | returnDescription = ' — ' + value.returnDescription
204 |
205 | description += ''
206 | description += '
Type
'
207 | description += '
' + returnValue + '' + returnDescription + '
'
208 | description += "
"
209 |
210 | return description
211 |
212 | ###*
213 | * @param {Array} typeArray
214 | *
215 | * @return {String}
216 | ###
217 | buildTypeSpecificationFromTypeArray: (typeArray) ->
218 | if typeArray.length == 0
219 | return '(Not known)'
220 |
221 | typeNames = typeArray.map (type) ->
222 | return type.type
223 |
224 | return typeNames.join('|')
225 |
--------------------------------------------------------------------------------
/lib/AbstractProvider.coffee:
--------------------------------------------------------------------------------
1 | $ = require 'jquery'
2 | SubAtom = require 'sub-atom'
3 |
4 | module.exports =
5 |
6 | ##*
7 | # Base class for providers.
8 | ##
9 | class AbstractProvider
10 | ###*
11 | * The class selectors for which a hover event will be triggered.
12 | ###
13 | hoverEventSelectors: ''
14 |
15 | ###*
16 | * The service (that can be used to query the source code and contains utility methods).
17 | ###
18 | service: null
19 |
20 | ###*
21 | * Keeps track of the currently pending promise.
22 | ###
23 | pendingPromise: null
24 |
25 | ###*
26 | * Initializes this provider.
27 | *
28 | * @param {mixed} service
29 | ###
30 | activate: (@service) ->
31 | dependentPackage = 'language-php'
32 |
33 | # It could be that the dependent package is already active, in that case we can continue immediately. If not,
34 | # we'll need to wait for the listener to be invoked
35 | if atom.packages.isPackageActive(dependentPackage)
36 | @doActualInitialization()
37 |
38 | atom.packages.onDidActivatePackage (packageData) =>
39 | return if packageData.name != dependentPackage
40 |
41 | @doActualInitialization()
42 |
43 | atom.packages.onDidDeactivatePackage (packageData) =>
44 | return if packageData.name != dependentPackage
45 |
46 | @deactivate()
47 |
48 | ###*
49 | * Does the actual initialization.
50 | ###
51 | doActualInitialization: () ->
52 | atom.workspace.observeTextEditors (editor) =>
53 | if /text.html.php$/.test(editor.getGrammar().scopeName)
54 | @registerEvents(editor)
55 |
56 | # When you go back to only have one pane the events are lost, so need to re-register.
57 | atom.workspace.onDidDestroyPane (pane) =>
58 | panes = atom.workspace.getPanes()
59 |
60 | if panes.length == 1
61 | @registerEventsForPane(panes[0])
62 |
63 | # Having to re-register events as when a new pane is created the old panes lose the events.
64 | atom.workspace.onDidAddPane (observedPane) =>
65 | panes = atom.workspace.getPanes()
66 |
67 | for pane in panes
68 | if pane != observedPane
69 | @registerEventsForPane(pane)
70 |
71 | atom.workspace.onDidStopChangingActivePaneItem (item) =>
72 | @removePopover()
73 |
74 | ###*
75 | * Registers the necessary event handlers for the editors in the specified pane.
76 | *
77 | * @param {Pane} pane
78 | ###
79 | registerEventsForPane: (pane) ->
80 | for paneItem in pane.items
81 | if atom.workspace.isTextEditor(paneItem)
82 | if /text.html.php$/.test(paneItem.getGrammar().scopeName)
83 | @registerEvents(paneItem)
84 |
85 | ###*
86 | * Deactives the provider.
87 | ###
88 | deactivate: () ->
89 | if @subAtom
90 | @subAtom.dispose()
91 | @subAtom = null
92 |
93 | @removePopover()
94 |
95 | ###*
96 | * Registers the necessary event handlers.
97 | *
98 | * @param {TextEditor} editor TextEditor to register events to.
99 | ###
100 | registerEvents: (editor) ->
101 | textEditorElement = atom.views.getView(editor)
102 | scrollViewElement = textEditorElement.querySelector('.scroll-view')
103 |
104 | if scrollViewElement?
105 | @getSubAtom().add scrollViewElement, 'mouseover', @hoverEventSelectors, (event) =>
106 | selector = @getSelectorFromEvent(event)
107 |
108 | if selector == null
109 | return
110 |
111 | editorViewComponent = atom.views.getView(editor).component
112 |
113 | # Ticket #140 - In rare cases the component is null.
114 | if editorViewComponent
115 | cursorPosition = editorViewComponent.screenPositionForMouseEvent(event)
116 |
117 | @removePopover()
118 | @showPopoverFor(editor, selector, cursorPosition)
119 |
120 | @getSubAtom().add scrollViewElement, 'mouseout', @hoverEventSelectors, (event) =>
121 | @removePopover()
122 |
123 | horizontalScrollbar = textEditorElement.querySelector('.horizontal-scrollbar')
124 |
125 | if horizontalScrollbar?
126 | @getSubAtom().add horizontalScrollbar, 'scroll', (event) =>
127 | @removePopover()
128 |
129 | verticalScrollbar = textEditorElement.querySelector('.vertical-scrollbar')
130 |
131 | if verticalScrollbar?
132 | @getSubAtom().add verticalScrollbar, 'scroll', (event) =>
133 | @removePopover()
134 |
135 | # Ticket #107 - Mouseout isn't generated until the mouse moves, even when scrolling (with the keyboard or
136 | # mouse). If the element goes out of the view in the meantime, its HTML element disappears, never removing
137 | # it.
138 | editor.onDidDestroy () =>
139 | @removePopover()
140 |
141 | editor.onDidStopChanging () =>
142 | @removePopover()
143 |
144 | ###*
145 | * Shows a popover containing the documentation of the specified element located at the specified location.
146 | *
147 | * @param {TextEditor} editor TextEditor containing the elemment.
148 | * @param {string} selector The selector to search for.
149 | * @param {Point} bufferPosition The cursor location the element is at.
150 | * @param {int} delay How long to wait before the popover shows up.
151 | * @param {int} fadeInTime The amount of time to take to fade in the tooltip.
152 | ###
153 | showPopoverFor: (editor, selector, bufferPosition, delay = 500, fadeInTime = 100) ->
154 | name = $(selector).text()
155 |
156 | successHandler = (tooltipText) =>
157 | @removePopover()
158 |
159 | if tooltipText?.length > 0
160 | popoverElement = @getPopoverElementFromSelector(selector)
161 |
162 | @attachedPopover = @service.createAttachedPopover(popoverElement)
163 | @attachedPopover.setText('' + tooltipText + '
')
164 | @attachedPopover.showAfter(delay, fadeInTime)
165 |
166 | failureHandler = () =>
167 | return
168 |
169 | @fetchTooltipForWord(editor, bufferPosition, name).then(successHandler, failureHandler)
170 |
171 | ###*
172 | * Removes the popover, if it is displayed.
173 | ###
174 | removePopover: () ->
175 | if @pendingPromise?
176 | @pendingPromise.reject()
177 | @pendingPromise = null
178 |
179 | if @attachedPopover
180 | @attachedPopover.dispose()
181 | @attachedPopover = null
182 |
183 | ###*
184 | * Retrieves a tooltip for the word given.
185 | *
186 | * @param {TextEditor} editor TextEditor to search for namespace of term.
187 | * @param {Point} bufferPosition The cursor location the term is at.
188 | * @param {string} name The name of the element to retrieve the tooltip for.
189 | *
190 | * @return {Promise}
191 | ###
192 | fetchTooltipForWord: (editor, bufferPosition, name) ->
193 | return new Promise (resolve, reject) =>
194 | @pendingPromise = {
195 | reject: reject
196 | }
197 |
198 | successHandler = (tooltipText) =>
199 | @pendingPromise = null
200 | resolve(tooltipText)
201 | return
202 |
203 | failureHandler = () =>
204 | @pendingPromise = null
205 | reject()
206 | return
207 |
208 | return @getTooltipForWord(editor, bufferPosition, name).then(successHandler, failureHandler)
209 |
210 | ###*
211 | * Retrieves a tooltip for the word given.
212 | *
213 | * @param {TextEditor} editor TextEditor to search for namespace of term.
214 | * @param {Point} bufferPosition The cursor location the term is at.
215 | * @param {string} name The name of the element to retrieve the tooltip for.
216 | *
217 | * @return {Promise}
218 | ###
219 | getTooltipForWord: (editor, bufferPosition, name) ->
220 | throw new Error("This method is abstract and must be implemented!")
221 |
222 | ###*
223 | * Gets the correct selector when a selector is clicked.
224 | * @param {jQuery.Event} event A jQuery event.
225 | * @return {object|null} A selector to be used with jQuery.
226 | ###
227 | getSelectorFromEvent: (event) ->
228 | return event.currentTarget
229 |
230 | ###*
231 | * Gets the correct element to attach the popover to from the retrieved selector.
232 | * @param {jQuery.Event} event A jQuery event.
233 | * @return {object|null} A selector to be used with jQuery.
234 | ###
235 | getPopoverElementFromSelector: (selector) ->
236 | return selector
237 |
238 | ###*
239 | * @return {Object}
240 | ###
241 | getSubAtom: () ->
242 | if not @subAtom
243 | @subAtom = new SubAtom()
244 |
245 | return @subAtom
246 |
--------------------------------------------------------------------------------