main.d.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. import type { TextEditor, Point, Range as AtomRange } from 'atom';
  2. type MaybePromise<T> = T | Promise<T>;
  3. // The properties that a provider is allowed to modify on a `SelectList`
  4. // instance during symbol retrieval.
  5. //
  6. // A provider that marks itself as `isExclusive: true` will be allowed to set
  7. // certain UI messages on the `SelectList`. This allows the provider to offer
  8. // UI guidance messages.
  9. //
  10. // For instance, if a project-wide symbol search is taking place, the provider
  11. // could set an `emptyMessage` of “Query must be at least X characters long” to
  12. // explain why no results are present at first.
  13. type ListControllerParams = Partial<{
  14. errorMessage: string,
  15. emptyMessage: string,
  16. loadingMessage: string,
  17. laodingBadge: string
  18. }>;
  19. type ListControllerParamName = keyof ListControllerParams;
  20. // An object given to the exclusive provider during symbol retrieval. The
  21. // provider can use this to control certain aspects of the symbol list's UI.
  22. type ListController = {
  23. // Set props on the `SelectList` instance.
  24. set (params: ListControllerParams): void,
  25. // Clear any number of props on the `SelectList` instance.
  26. clear(...propNames: ListControllerParamName[]): void
  27. };
  28. export type SymbolPosition = {
  29. // An instance of `Point` describing the symbol's location. The `column`
  30. // value of the point may be ignored, depending on the user's settings. At
  31. // least one of `position` and `range` must exist.
  32. position: Point;
  33. };
  34. export type SymbolRange = {
  35. // An exact range describing the bounds of a given token. If present, might
  36. // be used to highlight the token when selected by the user, though that
  37. // depends on the user's settings. At least one of `position` and `range`
  38. // must exist.
  39. range: AtomRange
  40. };
  41. export type SymbolDirectoryAndFile = {
  42. // The name of the file that contains the symbol. Will be shown in the UI.
  43. file: string,
  44. // The path of to the directory of the file that contains the symbol. Should
  45. // not contain the file name.
  46. directory: string
  47. };
  48. export type SymbolPath = {
  49. // The full path to the file that contains the symbol.
  50. path: string
  51. };
  52. // The only required fields in a file symbol are (a) the `name` of the symbol,
  53. // and (b) a reference to its row in the buffer (via either `position` or
  54. // `range`). Other fields are optional, but make for a richer UI presentation.
  55. export type FileSymbol = (SymbolPosition | SymbolRange) & {
  56. // The name of the symbol. This value will be shown in the UI and will be
  57. // filtered against if the user types in the text box. Required.
  58. name: string,
  59. // A word representing the symbol in some way. Typically this would describe
  60. // the symbol — function, constant, et cetera — but can be used however the
  61. // provider sees fit. If present, will be included in the symbol list as a
  62. // badge.
  63. tag?: string
  64. // A _short_ string of explanatory text. Optional. Can be used for text that
  65. // is contexually significant to the symbol; for instance, a method or field
  66. // might describe the class that owns it. Symbol consumers will expect this
  67. // field to be short, and will not devote much space to it in the interface,
  68. // so this field _should not_ contain unbounded text.
  69. context?: string
  70. // POSSIBLE ENHANCEMENTS (UNIMPLEMENTED!):
  71. //
  72. // I don't necessarily find these useful myself, or at least not useful
  73. // enough to warrant their inclusion in a space-constrained list of symbols,
  74. // but some people might want these to be present.
  75. // A description of the symbol in code or pseudocode. For functions, this
  76. // could be a function signature, along with parameter names and (if known)
  77. // types.
  78. //
  79. // This field would receive its own line in a symbol list.
  80. signature?: string
  81. // The literal line of code containing the symbol. A symbol consumer could
  82. // try to retrieve this information itself, but some symbol providers would
  83. // be able to supply it much more simply.
  84. //
  85. // This field would receive its own line in a symbol list.
  86. source?: string
  87. };
  88. // A project symbol has the additional requirement of specifying the file in
  89. // which each symbol is located, via either the `path` property or both
  90. // `directory` and `file`.
  91. export type ProjectSymbol = FileSymbol & (SymbolDirectoryAndFile | SymbolPath);
  92. // Metadata received by a symbol provider as part of a call to
  93. // `canProvideSymbols` or `getSymbols`.
  94. export type SymbolMeta = {
  95. // The type of action being performed:
  96. //
  97. // * `file`: A symbol search within the current file.
  98. // * `project`: A project-wide symbol search.
  99. // * `project-find`: A project-wide attempt to resolve a reference based on
  100. // (a) the position of the cursor, (b) the value of the editor's current
  101. // text selection, or (c) whatever word was clicked on in the IDE.
  102. type: 'file' | 'project' | 'project-find',
  103. // The current text editor.
  104. editor: TextEditor,
  105. // The relevant search term, if any.
  106. //
  107. // When `type` is `project`, this will represent the text that the user has
  108. // typed into a search field in order to filter the list of symbols.
  109. //
  110. // When `type` is `project-find`, this will represent the text that the IDE
  111. // wants to resolve.
  112. //
  113. // When `type` is `file`, this field will be absent, because file symbols are
  114. // queried only initially, before the user has typed anything; all winnowing
  115. // is done on the frontend as the user types.
  116. query?: string,
  117. // The relevant range in the buffer.
  118. //
  119. // This may be present when `type` is `project-find` and the consumer wants
  120. // to resolve an arbitrary buffer range instead of the word under the cursor.
  121. range?: Range,
  122. // An `AbortSignal` that represents whether the task has been cancelled. This
  123. // will happen if the user cancels out of the symbol UI while waiting for
  124. // symbols, or if they type a new character in the query field before the
  125. // results have returned for the previous typed character. It will also
  126. // happen if the provider exceeds the amount of time allotted to it (see
  127. // `timeoutMs` below).
  128. //
  129. // If the provider goes async at any point, it should check the signal after
  130. // resuming. If the signal has aborted, then there is no point in continuing.
  131. // The provider should immediately return/resolve with `null` and avoid doing
  132. // unnecessary further work.
  133. signal: AbortSignal,
  134. // The amount of time, in milliseconds, the provider has before it must
  135. // return results. This value is configurable by the user. If the provider
  136. // doesn't return anything after this amount of time, it will be ignored.
  137. //
  138. // The provider is not in charge of ensuring that it returns results within
  139. // this amount of time; `symbols-view` enforces that on its own. This value
  140. // is given to providers so that they can act wisely when faced with a choice
  141. // between “search for more symbols” and “return what we have.”
  142. //
  143. // The `timeoutMs` property is only present when the appropriate symbol list
  144. // UI is not yet present. Its purpose is to show the UI within a reasonable
  145. // amount of time. If the UI is already present — for instance, when
  146. // winnowing results in a project-wide symbol search — `timeoutMs` will be
  147. // omitted, and the provider can take as much time as it deems appropriate.
  148. timeoutMs?: number
  149. };
  150. type FileSymbolMeta = SymbolMeta & { type: 'file' };
  151. type ProjectSymbolMeta = SymbolMeta & { type: 'project' | 'project-find' };
  152. // Symbol metadata that will be passed to the `canProvideSymbols` method.
  153. export type PreliminarySymbolMeta = Omit<SymbolMeta, 'signal'>;
  154. export interface SymbolProvider {
  155. // A human-readable name for your provider. This name may be displayed to the
  156. // user, and it's how they can configure `symbols-view` to prefer
  157. // certain providers over others.
  158. name: string,
  159. // The name of your package. This is present so that the user can find out
  160. // where a given provider comes from. In the settings for preferring certain
  161. // providers over others, a package name can be specified instead of a
  162. // specific provider name — either because that's the value the user
  163. // remembers, or because the package contains several providers and the user
  164. // wishes to express a preference for all those providers at once.
  165. packageName: string,
  166. // If present, will be called on window teardown, or if `symbols-view`
  167. // or the provider's own package is disabled.
  168. destroy?(): void,
  169. // An optional method. If it exists, the main package will use it to register
  170. // a callback so that it can clear the cache of this provider's symbols.
  171. //
  172. // The main package will automatically clear its cache for these reasons:
  173. //
  174. // * when the main package's config changes (entire cache);
  175. // * when any provider is activated or deactivated (single provider's cache);
  176. // * when the buffer is modified in any of several ways, including grammar
  177. // change, save, or buffer change (entire cache).
  178. //
  179. // If your provider may have its cache invalidated for reasons not in this
  180. // list, you should implement `onShouldClearCache` and invoke any callback
  181. // that registers for it. The `EventEmitter` pattern found throughout Pulsar
  182. // is probably how you want to pull this off.
  183. onShouldClearCache?(callback: () => TextEditor): void,
  184. // Whether this provider aims to be the main symbol provider for a given
  185. // file. The “exclusive” provider competes with the other workhorse providers
  186. // of symbols like `ctags` and Tree-sitter to offer typical symbols like
  187. // classes, method names, and the like. A maximum of one exclusive provider
  188. // will be chosen for any task, depending on which one scores highest.
  189. //
  190. // “Supplemental” providers are those that contribute more specialized kinds
  191. // of symbols. These providers generally do not compete with exclusive
  192. // providers, or with each other, and can add symbols to any exclusive
  193. // provider’s results.
  194. isExclusive?: boolean,
  195. // Indicates whether the provider can provide symbols for a given task. Can
  196. // return either a boolean or a number; boolean `true` is equivalent to a
  197. // score of `1`, and boolean `false` is equivalent to a score of `0`.
  198. //
  199. // This method receives the same metadata bundle that will be present in the
  200. // subsequent call to `getSymbols`. The provider can inspect this metadata
  201. // and decide whether it can fulfill the given symbols request. It _should
  202. // not_ start the task of gathering symbols; the point of this method is to
  203. // determine which provider is best for the task without starting the work.
  204. //
  205. // Examples:
  206. //
  207. // * A provider that can analyze the current file, but not the entire
  208. // project, should return `false` for any requests where `type` does not
  209. // equal `file`.
  210. // * A provider that works by analyzing code on disk, rather than looking at
  211. // the current unsaved contents of buffers, could return a slightly lower
  212. // score if asked to complete symbols for a file that has been modified.
  213. // This would indicate that it’d be a slightly worse than usual candidate.
  214. // * If my provider can do project-wide symbol search but _can't_ do a
  215. // go-to-definition lookup, it can still serve as a fallback provider when
  216. // `type` is `project-find`. But it should return a lower score to reflect
  217. // that it's not the ideal choice.
  218. //
  219. // Since language server providers will have to ask their servers about
  220. // capabilities, this method can go async, though it’s strongly suggested to
  221. // keep it synchronous if possible. (The `timeoutMs` argument isn't yet
  222. // enforced on `canProvideSymbols`, but we reserve the right to start
  223. // enforcing it at any point without a bump in the service's version number.)
  224. //
  225. // To avoid a number war, any numeric value greater than `1` returned from
  226. // `canProvideSymbols` will be clamped to `1`. The user can break ties by
  227. // choosing their preferred providers in the package settings.
  228. canProvideSymbols(meta: PreliminarySymbolMeta): MaybePromise<boolean | number>,
  229. // Returns a list of symbols.
  230. //
  231. // If there are no results, you should return an empty array. If the request
  232. // is invalid or cannot be completed — for instance, if the user cancels the
  233. // task — you should return `null`.
  234. //
  235. // The second argument, `listController`, will be present _only_ for the
  236. // provider marked with `isExclusive: true`. It allows the provider to set
  237. // and clear UI messages if needed. Supplemental providers don't receive this
  238. // argument.
  239. //
  240. // This method can go async if needed. Whenever it performs an async task, it
  241. // should check `meta.signal` afterward to see if it should cancel.
  242. //
  243. // The `type` property of `meta` affects which symbols this method should
  244. // return:
  245. //
  246. // * If `type` is `file`, this method should return a full list of symbols
  247. // for the current file.
  248. //
  249. // * If `type` is `project`, this method should return an _appropriate_ list
  250. // of symbols for the project. The ideal approach would be to return only
  251. // those results that match `meta.query`; you may choose not to return any
  252. // symbols at all until `meta.query` is of a minimum length. But you may
  253. // also return a full list of project symbols and rely on `symbols-view` to
  254. // do all of the filtering as the user types. (In the latter case,
  255. // `getSymbols` will still be called after each new keystroke; a future
  256. // version of this service may offer a way to control that behavior.)
  257. //
  258. // If you return an empty list when `meta.query` is too short, you should
  259. // use `listController` to set a message in the UI so that users understand
  260. // why.
  261. //
  262. // * If `type` is `project-find`, the user is trying to find where
  263. // `meta.query` is defined (a go-to-definition request). If this provider
  264. // knows how to do that, it should find the answer and return it as the
  265. // _only_ symbol in the returned list. If it doesn't, it is allowed to
  266. // treat this similarly to a project-wide symbol search and return more
  267. // than one result.
  268. //
  269. getSymbols(meta: FileSymbolMeta, listController?: ListController): MaybePromise<FileSymbol[] | null>
  270. getSymbols(meta: ProjectSymbolMeta, listController?: ListController): MaybePromise<ProjectSymbol[] | null>
  271. }
  272. export type SymbolProviderMainModule = {
  273. activate(): void,
  274. deactivate(): void,
  275. // No business logic should go in here. If a package wants to provide symbols
  276. // only under certain circumstances, it should decide those circumstances on
  277. // demand, rather than return this provider only conditionally.
  278. //
  279. // A provider author may argue that they should be allowed to inspect the
  280. // environment before deciding what (or if) to return — but anything they'd
  281. // inspect is something that can change mid-session. Too complicated. All
  282. // provider decisions can get decided at runtime.
  283. //
  284. // So, for instance, if a certain provider only works with PHP files, it
  285. // should return its instance here no matter what, and that instance can
  286. // respond to `canProvideSymbols` with `false` if the given editor isn't
  287. // using a PHP grammar. It shouldn't try to get clever and bail out entirely
  288. // if, say, the project doesn't have any PHP files on load — because, of
  289. // course, it _could_ add a PHP file at any point, and we're not going to
  290. // revisit the decision later.
  291. //
  292. // Likewise, if a provider depends upon a language server that may or may not
  293. // be running, it should not try to be clever about what it returns from
  294. // `provideSymbols`. Instead, it should return early from `canProvideSymbols`
  295. // when the language server isn't running.
  296. //
  297. // A single package can supply multiple providers if need be.
  298. //
  299. provideSymbols(): SymbolProvider | SymbolProvider[],
  300. };