|
| 1 | +# `autocomplete.provider` service documentation |
| 2 | + |
| 3 | +Read this document if you’d like to write your own autocompletion provider. |
| 4 | + |
| 5 | +If you want to turn a language server into an autocompletion provider, you can use `atom-languageclient` instead of providing this service directly. [Consult its README](https://github.com/savetheclocktower/atom-languageclient?tab=readme-ov-file#developing-packages) for more information. |
| 6 | + |
| 7 | +## Service definition |
| 8 | + |
| 9 | +The following TypeScript-style code block describes the `autocomplete.provider` service in its current form. |
| 10 | + |
| 11 | +The current service version is **5.1.0**. The documentation is accurate for version 4 or greater of the API; aspects that are present only in versions later than 4 are annotated as such. |
| 12 | + |
| 13 | +```ts |
| 14 | +import { Point, Range, ScopeDescriptor, TextEditor } from 'atom'; |
| 15 | + |
| 16 | + |
| 17 | +/** |
| 18 | + * A {@link Range} or any object that can be accepted by {@link |
| 19 | + * Range.prototype.fromObject}. |
| 20 | + */ |
| 21 | +type RangeCompatible = |
| 22 | + | Range |
| 23 | + | [Point, Point], |
| 24 | + | [[number, number], [number, number]] |
| 25 | + | [{ row: number, column: number}, { row: number, column: number}] |
| 26 | + |
| 27 | +/** |
| 28 | + * Describes a range of a buffer and the text to insert into it. This is one |
| 29 | + * possible insertion strategy among several for a suggestion. |
| 30 | + * |
| 31 | + * Like a `Range` from the Language Server Protocol specification, but with an |
| 32 | + * Atom-style {@link Range}. |
| 33 | + * |
| 34 | + * This type bears some similarity to an existing `TextEdit` type used by |
| 35 | + * Atom-IDE. The existing type uses the `oldRange` property instead of `range`, |
| 36 | + * but is otherwise identical. Both are acceptable. |
| 37 | + */ |
| 38 | +type TextEdit = { |
| 39 | + newText: string |
| 40 | +} & ({ range: RangeCompatible } | { oldRange: RangeCompatible }) |
| 41 | + |
| 42 | +/** |
| 43 | + * Known types of suggestions; these have predefined styles when rendered. |
| 44 | + */ |
| 45 | +type SuggestionType = |
| 46 | + | 'variable' |
| 47 | + | 'constant' |
| 48 | + | 'property' |
| 49 | + | 'value' |
| 50 | + | 'method' |
| 51 | + | 'function' |
| 52 | + | 'class' |
| 53 | + | 'type' |
| 54 | + | 'keyword' |
| 55 | + | 'tag' |
| 56 | + | 'snippet' |
| 57 | + | 'import' |
| 58 | + | 'require' |
| 59 | + |
| 60 | +/** |
| 61 | + * A single suggestion as returned by `getSuggestions` or |
| 62 | + * `getSuggestionDetailsOnSelect`. |
| 63 | + */ |
| 64 | +type Suggestion = { |
| 65 | + // A suggestion can be inserted one of several ways: as plain text, as a |
| 66 | + // snippet, or as a `TextEdit`. One of these three properties is therefore |
| 67 | + // required; the others are optional. |
| 68 | + // |
| 69 | + // Of these three, later properties win out over earlier ones. For example, |
| 70 | + // `textEdit` will be preferred over both `snippet` and `text` if it is |
| 71 | + // present. |
| 72 | + |
| 73 | + /** |
| 74 | + * The text to be inserted. Will be used if `snippet` is absent. |
| 75 | + */ |
| 76 | + text?: string |
| 77 | + |
| 78 | + /** |
| 79 | + * A snippet to insert upon suggestion selection. |
| 80 | + */ |
| 81 | + snippet?: string |
| 82 | + |
| 83 | + /** |
| 84 | + * Text edit to make when the item is chosen. Use this when you want to be |
| 85 | + * specific about which range of the buffer is replaced. When present, this |
| 86 | + * will be used as the insertion strategy instead of the default behavior. |
| 87 | + * |
| 88 | + * If a suggestion needs to make several edits upon insertion, the rest can |
| 89 | + * be specified via `additionalTextEdits`. |
| 90 | + * |
| 91 | + * Added in 5.1.0. |
| 92 | + */ |
| 93 | + textEdit?: TextEdit |
| 94 | + |
| 95 | + /** |
| 96 | + * The text to show in the menu for this suggestion. Optional; falls back to |
| 97 | + * `snippet`, then `text`. |
| 98 | + */ |
| 99 | + displayText?: string |
| 100 | + |
| 101 | + /** |
| 102 | + * A list of `Range`s to replace when inserting the text. Each `Range`present |
| 103 | + * in this list will result in one insertion of the suggestion's textor |
| 104 | + * snippet. |
| 105 | + * |
| 106 | + * Can insert the autocompletion's text/snippet into one specific range or |
| 107 | + * multiple. Use this when you know the exact range of the current buffer |
| 108 | + * that should be replaced with the given text. |
| 109 | + * |
| 110 | + * Added in 5.0.0. Has no effect if `textEdit` is specified. |
| 111 | + */ |
| 112 | + ranges?: Range[] |
| 113 | + |
| 114 | + /** |
| 115 | + * Text before the cursor that should be replaced as part of the insertion of |
| 116 | + * this suggestion. Optional; if omitted, the prefix before the cursor will |
| 117 | + * be used. Has no effect if `textEdit` or `ranges` is specified. |
| 118 | + */ |
| 119 | + replacementPrefix?: string |
| 120 | + |
| 121 | + /** |
| 122 | + * A "type" for this suggestion. Used to classify suggestions and distinguish |
| 123 | + * them visually. The types of {@link SuggestionType} are preferred (and have |
| 124 | + * predefined styles), but you can use an arbitrary string if none of those |
| 125 | + * types suffice. |
| 126 | + */ |
| 127 | + type?: SuggestionType | string |
| 128 | + |
| 129 | + /** |
| 130 | + * Text edits to make when the item is chosen — in addition to the main item. |
| 131 | + * |
| 132 | + * These are typically optional edits, such as an automatic `import` |
| 133 | + * statement that's inserted when a suggestion warrants it. |
| 134 | + * |
| 135 | + * When present, these edits are made in all code paths, regardless of the |
| 136 | + * original insertion strategy. |
| 137 | + * |
| 138 | + * Added in 5.1.0. |
| 139 | + */ |
| 140 | + additionalTextEdits?: TextEdit |
| 141 | + |
| 142 | + /** |
| 143 | + * A label to display before the suggestion. This can indicate useful |
| 144 | + * information like a method return type. Both text and HTML variants are |
| 145 | + * supported; `leftLabelHTML` takes precedence over `leftLabel` when both are |
| 146 | + * present. |
| 147 | + */ |
| 148 | + leftLabel?: string |
| 149 | + leftLabelHTML?: string |
| 150 | + |
| 151 | + /** |
| 152 | + * A label to display after the suggestion. This can indicate useful |
| 153 | + * information like a type annotation. Both text and HTML variants are |
| 154 | + * supported; `rightLabelHTML` takes precedence over `rightLabel` when both |
| 155 | + * are present. |
| 156 | + */ |
| 157 | + rightLabel?: string |
| 158 | + rightLabelHTML?: string |
| 159 | + |
| 160 | + /** |
| 161 | + * Class name to add to the suggestion's row in the HTML. Allows for further |
| 162 | + * styling customization, if needed. |
| 163 | + */ |
| 164 | + className?: string |
| 165 | + |
| 166 | + /** |
| 167 | + * An override to allow you to specify your own icon. Should follow Octicon |
| 168 | + * conventions; e.g., `"<i class="icon-move-right"></i>"`. Optional. |
| 169 | + */ |
| 170 | + iconHTML?: string |
| 171 | + |
| 172 | + /** |
| 173 | + * A docstring summary or short description of the suggestion. When |
| 174 | + * specified, it will be displayed at the bottom of the suggestions list. |
| 175 | + * Optional. |
| 176 | + */ |
| 177 | + description?: string |
| 178 | + |
| 179 | + /** |
| 180 | + * A url to the documentation or more information about this suggestion. When |
| 181 | + * specified, a `More…` link will be displayed in the description area. |
| 182 | + */ |
| 183 | + descriptionMoreURL?: string |
| 184 | + |
| 185 | + /** |
| 186 | + * A list of indices where the characters in the prefix appear in this |
| 187 | + * suggestion's text. |
| 188 | + * @type {Object} |
| 189 | + */ |
| 190 | + characterMatchIndices?: number[] |
| 191 | + |
| 192 | + // (Either `text`, `snippet`, or `textEdit` must be provided.) |
| 193 | +} & { text: string } | { snippet: string } | { textEdit: TextEdit}; |
| 194 | + |
| 195 | +/** |
| 196 | + * The provider object that you should make available to `autocomplete-plus`. |
| 197 | + * This should be the return value of whatever method you specified in your |
| 198 | + * `providedServices` metadata. |
| 199 | + */ |
| 200 | +type ServiceProvider = { |
| 201 | + /** |
| 202 | + * Selector for which this provider should be active. Multiple values can be |
| 203 | + * given separated by commas. |
| 204 | + */ |
| 205 | + selector: string, |
| 206 | + /** |
| 207 | + * Selector for which this provider should be inactive, even if scope |
| 208 | + * otherwise matches `selector`. Multiple values can be given separated by |
| 209 | + * commas. |
| 210 | + */ |
| 211 | + disableForSelector: string, |
| 212 | + |
| 213 | + /** |
| 214 | + * The priority of this provider relative to others. Higher numbers beat |
| 215 | + * lower numbers. |
| 216 | + */ |
| 217 | + inclusionPriority: number, |
| 218 | + |
| 219 | + /** |
| 220 | + * When `true`, this provider excludes options from providers with a lower |
| 221 | + * priority from even appearing in the menu. |
| 222 | + */ |
| 223 | + excludeLowerPriority: boolean, |
| 224 | + |
| 225 | + /** |
| 226 | + * The priority of this provider's suggestions relative to other suggestions |
| 227 | + * that may exist in the list. Influences the ordering of suggestions within |
| 228 | + * a menu. |
| 229 | + */ |
| 230 | + suggestionPriority: number, |
| 231 | + |
| 232 | + /** |
| 233 | + * When `true`, `autocomplete-plus` expects to receive many suggestions and |
| 234 | + * will filter the list based on what's already been typed in the token. When |
| 235 | + * `false`, you assert that whatever you deliver to `autocomplete-plus` has |
| 236 | + * already been filtered. |
| 237 | + */ |
| 238 | + filterSuggestions: boolean, |
| 239 | + |
| 240 | + /** |
| 241 | + * Retrieves suggestions for a given editor at a given point. Can consult |
| 242 | + * other metadata. Can go async. |
| 243 | + */ |
| 244 | + getSuggestions(meta: { |
| 245 | + /** The current text editor. */ |
| 246 | + editor: TextEditor, |
| 247 | + /** The position of the cursor. */ |
| 248 | + bufferPosition: Point, |
| 249 | + /** |
| 250 | + * The scope descriptor at the given buffer position. |
| 251 | + * @see https://docs.pulsar-edit.dev/api/pulsar/latest/ScopeDescriptor/ |
| 252 | + */ |
| 253 | + scopeDescriptor: ScopeDescriptor, |
| 254 | + /** |
| 255 | + * The prefix that the user has typed before the cursor. Typically |
| 256 | + * represents all word-like characters between the cursor and the last |
| 257 | + * non-word character. |
| 258 | + */ |
| 259 | + prefix: string, |
| 260 | + /** |
| 261 | + * Whether the user activated this menu manually or had it appear |
| 262 | + * automatically while typing. |
| 263 | + */ |
| 264 | + activatedManually: boolean |
| 265 | + }): Suggestion[] | Promise<Suggestion[]>, |
| 266 | + |
| 267 | + /** |
| 268 | + * Fills in further details on this suggestion when it is highlighted in the |
| 269 | + * menu. Optional. |
| 270 | + * |
| 271 | + * A language server can use this method to send `completionItem/resolve` and |
| 272 | + * return an updated suggestion with the new data. |
| 273 | + */ |
| 274 | + getSuggestionDetailsOnSelect?(suggestion: Suggestion): Promise<Suggestion>, |
| 275 | + |
| 276 | + /** |
| 277 | + * Invoked after a chosen suggestion is inserted into the editor. Optional. |
| 278 | + */ |
| 279 | + onDidInsertSuggestion?(meta: { |
| 280 | + /** The current text editor. */ |
| 281 | + editor: TextEditor, |
| 282 | + /** The position of the cursor when the suggestion was chosen. */ |
| 283 | + triggerPosition: Point, |
| 284 | + /** The suggestion that was chosen. */ |
| 285 | + suggestion: Suggestion |
| 286 | + }): void; |
| 287 | + |
| 288 | + /** |
| 289 | + * Called when your provider needs to be cleaned up. Optional. |
| 290 | + */ |
| 291 | + dispose?(): void; |
| 292 | +} |
| 293 | + |
| 294 | +``` |
| 295 | + |
| 296 | + |
| 297 | +## Registering your provider with `autocomplete-plus` |
| 298 | + |
| 299 | +In your `package.json`, add: |
| 300 | + |
| 301 | +```json |
| 302 | +"providedServices": { |
| 303 | + "autocomplete.provider": { |
| 304 | + "versions": { |
| 305 | + "4.0.0": "provideAutocomplete" |
| 306 | + } |
| 307 | + } |
| 308 | +} |
| 309 | +``` |
| 310 | + |
| 311 | +You may call this value whatever you like; `provideAutocomplete` is a suggestion. |
| 312 | + |
| 313 | +Then, in your main package export, define a method of the same name: |
| 314 | + |
| 315 | +```js |
| 316 | +module.exports = { |
| 317 | + activate() { |
| 318 | + // existing activation code |
| 319 | + } |
| 320 | + |
| 321 | + provideAutocomplete() { |
| 322 | + // Return a value that conforms to the `ServiceProvider` interface |
| 323 | + // described above… |
| 324 | + return new Provider() |
| 325 | + |
| 326 | + // …or return multiple such providers as an array. |
| 327 | + return [new Provider(), new OtherProvider()] |
| 328 | + } |
| 329 | +} |
| 330 | +``` |
| 331 | + |
| 332 | + |
| 333 | +## Tips |
| 334 | + |
| 335 | +`autocomplete-plus` guesses at the “prefix” — that is, the range of characters before the cursor that might be part of whatever suggestion you will insert. |
| 336 | + |
| 337 | +For some languages, you may need to override this by specifying a `replacementPrefix` value for each suggestion: |
| 338 | + |
| 339 | +```js |
| 340 | +let provider = { |
| 341 | + selector: 'source.js', |
| 342 | + getSuggestions({ editor, bufferPosition }) { |
| 343 | + let prefix = this.getPrefix(editor, bufferPosition) |
| 344 | + }, |
| 345 | + |
| 346 | + getPrefix(editor, bufferPosition) { |
| 347 | + // Whatever your prefix regex might be. |
| 348 | + let regex = /[\w0-9_-]+$/ |
| 349 | + |
| 350 | + // Get the text for the line up to the triggered buffer position. |
| 351 | + let line = line = editor.getTextInRange([ |
| 352 | + [bufferPosition.row, 0], |
| 353 | + bufferPosition |
| 354 | + ]) |
| 355 | + |
| 356 | + // Match the regex to the line, and return the match (if any). |
| 357 | + return line.match(regex)?.[0] ?? '' |
| 358 | + } |
| 359 | + |
| 360 | +} |
| 361 | +``` |
0 commit comments