Skip to content
This repository was archived by the owner on Dec 15, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions .babelrc

This file was deleted.

17 changes: 0 additions & 17 deletions .eslintrc.js

This file was deleted.

13 changes: 0 additions & 13 deletions .flowconfig

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
// @flow

import {
LanguageClientConnection,
type ApplyWorkspaceEditParams,
type ApplyWorkspaceEditResponse,
ApplyWorkspaceEditParams,
ApplyWorkspaceEditResponse,
} from '../languageclient';
import { TextBuffer, TextEditor } from 'atom';
import * as atomIde from 'atom-ide';
import Convert from '../convert';

// Public: Adapts workspace/applyEdit commands to editors.
export default class ApplyEditAdapter {
// Public: Attach to a {LanguageClientConnection} to receive edit events.
static attach(connection: LanguageClientConnection) {
connection.onApplyEdit(m => ApplyEditAdapter.onApplyEdit(m));
public static attach(connection: LanguageClientConnection) {
connection.onApplyEdit((m) => ApplyEditAdapter.onApplyEdit(m));
}

static async onApplyEdit(params: ApplyWorkspaceEditParams): Promise<ApplyWorkspaceEditResponse> {
public static async onApplyEdit(params: ApplyWorkspaceEditParams): Promise<ApplyWorkspaceEditResponse> {

let changes = params.edit.changes || {};

if (params.edit.documentChanges) {
changes = {};
params.edit.documentChanges.forEach(change => {
params.edit.documentChanges.forEach((change) => {
if (change && change.textDocument) {
changes[change.textDocument.uri] = change.edits;
}
Expand All @@ -30,7 +30,7 @@ export default class ApplyEditAdapter {
const uris = Object.keys(changes);
const paths = uris.map(Convert.uriToPath);
const editors = await Promise.all(
paths.map(path => {
paths.map((path) => {
return atom.workspace.open(path, {
searchAllPanes: true,
// Open new editors in the background.
Expand All @@ -43,7 +43,7 @@ export default class ApplyEditAdapter {
const checkpoints = [];
try {
for (let i = 0; i < editors.length; i++) {
const editor = editors[i];
const editor = editors[i] as TextEditor;
const uri = uris[i];
// Get an existing editor for the file, or open a new one if it doesn't exist.
const edits = Convert.convertLsTextEdits(changes[uri]);
Expand Down Expand Up @@ -74,7 +74,7 @@ export default class ApplyEditAdapter {
}

// Private: Do some basic sanity checking on the edit ranges.
static validateEdit(buffer: atom$TextBuffer, edit: atomIde$TextEdit, prevEdit: ?atomIde$TextEdit): void {
private static validateEdit(buffer: TextBuffer, edit: atomIde.TextEdit, prevEdit: atomIde.TextEdit | null): void {
const path = buffer.getPath() || '';
if (prevEdit != null && edit.oldRange.end.compare(prevEdit.oldRange.start) > 0) {
throw Error(`Found overlapping edit ranges in ${path}`);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,47 @@
// @flow

import {
CompletionItemKind,
CompletionTriggerKind,
InsertTextFormat,
LanguageClientConnection,
type CompletionContext,
type CompletionItem,
type CompletionList,
type CompletionParams,
type ServerCapabilities,
type TextEdit,
CompletionContext,
CompletionItem,
CompletionList,
CompletionParams,
ServerCapabilities,
TextEdit,
} from '../languageclient';
import {Point} from 'atom';
import {CancellationTokenSource} from 'vscode-jsonrpc';
import {type ActiveServer} from '../server-manager';
import {
AutocompleteSuggestion,
AutocompleteRequest,
Point,
TextEditor,
} from 'atom';
import { CancellationTokenSource } from 'vscode-jsonrpc';
import { ActiveServer } from '../server-manager';
import Convert from '../convert';
import {filter} from 'fuzzaldrin-plus';
import { filter } from 'fuzzaldrin-plus';
import Utils from '../utils';

type SuggestionCacheEntry = {
isIncomplete: boolean,
triggerPoint: atom$Point,
suggestionMap: Map<atom$AutocompleteSuggestion, [CompletionItem, boolean]>,
};
interface SuggestionCacheEntry {
isIncomplete: boolean;
triggerPoint: Point;
suggestionMap: Map<AutocompleteSuggestion, [CompletionItem, boolean]>;
}

// Public: Adapts the language server protocol "textDocument/completion" to the Atom
// AutoComplete+ package.
export default class AutocompleteAdapter {
static canAdapt(serverCapabilities: ServerCapabilities): boolean {
public static canAdapt(serverCapabilities: ServerCapabilities): boolean {
return serverCapabilities.completionProvider != null;
}

static canResolve(serverCapabilities: ServerCapabilities): boolean {
return serverCapabilities.completionProvider != null && serverCapabilities.completionProvider.resolveProvider === true;
public static canResolve(serverCapabilities: ServerCapabilities): boolean {
return serverCapabilities.completionProvider != null &&
serverCapabilities.completionProvider.resolveProvider === true;
}

_suggestionCache: WeakMap<ActiveServer, SuggestionCacheEntry> = new WeakMap();
_cancellationTokens: WeakMap<LanguageClientConnection, CancellationTokenSource> = new WeakMap();
private _suggestionCache: WeakMap<ActiveServer, SuggestionCacheEntry> = new WeakMap();
private _cancellationTokens: WeakMap<LanguageClientConnection, CancellationTokenSource> = new WeakMap();

// Public: Obtain suggestion list for AutoComplete+ by querying the language server using
// the `textDocument/completion` request.
Expand All @@ -49,12 +53,14 @@ export default class AutocompleteAdapter {
//
// Returns a {Promise} of an {Array} of {atom$AutocompleteSuggestion}s containing the
// AutoComplete+ suggestions to display.
async getSuggestions(
public async getSuggestions(
server: ActiveServer,
request: atom$AutocompleteRequest,
onDidConvertCompletionItem?: (CompletionItem, atom$AutocompleteSuggestion, atom$AutocompleteRequest) => void,
): Promise<Array<atom$AutocompleteSuggestion>> {
const triggerChars = server.capabilities.completionProvider != null ? server.capabilities.completionProvider.triggerCharacters || [] : [];
request: AutocompleteRequest,
onDidConvertCompletionItem?: (CompletionItem, AutocompleteSuggestion, AutocompleteRequest) => void,
): Promise<AutocompleteSuggestion[]> {
const triggerChars =
server.capabilities.completionProvider != null ?
server.capabilities.completionProvider.triggerCharacters || [] : [];
const triggerPoint = AutocompleteAdapter.getTriggerPoint(request, triggerChars);
const prefixWithTrigger = AutocompleteAdapter.getPrefixWithTrigger(request, triggerPoint);

Expand All @@ -68,8 +74,13 @@ export default class AutocompleteAdapter {
}

// We either don't have suggestions or they are incomplete so request from the language server
const completions = await Utils.doWithCancellationToken(server.connection, this._cancellationTokens, cancellationToken =>
server.connection.completion(AutocompleteAdapter.createCompletionParams(request, prefixWithTrigger[0]), cancellationToken),
const completions =
await Utils.doWithCancellationToken(
server.connection,
this._cancellationTokens,
(cancellationToken) =>
server.connection.completion(
AutocompleteAdapter.createCompletionParams(request, prefixWithTrigger[0]), cancellationToken),
);
const isIncomplete = !Array.isArray(completions) && completions.isIncomplete;
const suggestionMap = this.completionItemsToSuggestions(completions, request, onDidConvertCompletionItem);
Expand All @@ -87,19 +98,20 @@ export default class AutocompleteAdapter {
// and a {atom$AutocompleteRequest} allowing you to adjust converted items.
//
// Returns a {Promise} of an {atom$AutocompleteSuggestion} with the resolved AutoComplete+ suggestion.
async completeSuggestion(
public async completeSuggestion(
server: ActiveServer,
suggestion: atom$AutocompleteSuggestion,
request: atom$AutocompleteRequest,
onDidConvertCompletionItem?: (CompletionItem, atom$AutocompleteSuggestion, atom$AutocompleteRequest) => void,
): Promise<atom$AutocompleteSuggestion> {
suggestion: AutocompleteSuggestion,
request: AutocompleteRequest,
onDidConvertCompletionItem?: (CompletionItem, AutocompleteSuggestion, AutocompleteRequest) => void,
): Promise<AutocompleteSuggestion> {
const cache = this._suggestionCache.get(server);
if (cache) {
const originalCompletionItem = cache.suggestionMap.get(suggestion);
if (originalCompletionItem != null && originalCompletionItem[1] === false) {
const resolvedCompletionItem = await server.connection.completionItemResolve(originalCompletionItem[0]);
if (resolvedCompletionItem != null) {
AutocompleteAdapter.completionItemToSuggestion(resolvedCompletionItem, suggestion, request, onDidConvertCompletionItem);
AutocompleteAdapter.completionItemToSuggestion(
resolvedCompletionItem, suggestion, request, onDidConvertCompletionItem);
originalCompletionItem[1] = true;
}
}
Expand All @@ -113,7 +125,7 @@ export default class AutocompleteAdapter {
//
// * `suggestions` An {Array} of {atom$AutocompleteSuggestion}s to set the replacementPrefix on.
// * `prefix` The {string} containing the prefix that should be set as replacementPrefix on all suggestions.
static setReplacementPrefixOnSuggestions(suggestions: Array<atom$AutocompleteSuggestion>, prefix: string): void {
public static setReplacementPrefixOnSuggestions(suggestions: AutocompleteSuggestion[], prefix: string): void {
for (const suggestion of suggestions) {
suggestion.replacementPrefix = prefix;
}
Expand All @@ -125,7 +137,7 @@ export default class AutocompleteAdapter {
// * `triggerChars` The {Array} of {string}s that can be trigger characters.
//
// Returns the {atom$Point} where the trigger occurred.
static getTriggerPoint(request: atom$AutocompleteRequest, triggerChars: Array<string>): atom$Point {
public static getTriggerPoint(request: AutocompleteRequest, triggerChars: string[]): Point {
if (triggerChars.includes(request.prefix)) {
return Point.fromObject(request.bufferPosition, true);
}
Expand All @@ -140,7 +152,7 @@ export default class AutocompleteAdapter {
// * `triggerPoint` The {atom$Point} where the trigger started.
//
// Returns a {string} containing the prefix including the trigger character.
static getPrefixWithTrigger(request: atom$AutocompleteRequest, triggerPoint: atom$Point): string {
public static getPrefixWithTrigger(request: AutocompleteRequest, triggerPoint: Point): string {
return request.editor
.getBuffer()
.getTextInRange([[triggerPoint.row, triggerPoint.column - 1], request.bufferPosition]);
Expand All @@ -156,7 +168,8 @@ export default class AutocompleteAdapter {
// * `textDocument` the language server protocol textDocument identification.
// * `position` the position within the text document to display completion request for.
// * `context` containing the trigger character and kind.
static createCompletionParams(request: atom$AutocompleteRequest, triggerCharacter: ?string): CompletionParams {
public static createCompletionParams(
request: AutocompleteRequest, triggerCharacter: string | null): CompletionParams {
return {
textDocument: Convert.editorToTextDocumentIdentifier(request.editor),
position: Convert.pointToPosition(request.bufferPosition),
Expand All @@ -171,7 +184,7 @@ export default class AutocompleteAdapter {
//
// Returns an {CompletionContext} that specifies the triggerKind and the triggerCharacter
// if there is one.
static createCompletionContext(triggerCharacter: ?string): CompletionContext {
public static createCompletionContext(triggerCharacter: string | null): CompletionContext {
return triggerCharacter == null
? {triggerKind: CompletionTriggerKind.Invoked}
: {triggerKind: CompletionTriggerKind.TriggerCharacter, triggerCharacter};
Expand All @@ -187,14 +200,17 @@ export default class AutocompleteAdapter {
// and a {atom$AutocompleteRequest} allowing you to adjust converted items.
//
// Returns a {Map} of AutoComplete+ suggestions ordered by the CompletionItems sortText.
completionItemsToSuggestions(
completionItems: Array<CompletionItem> | CompletionList,
request: atom$AutocompleteRequest,
onDidConvertCompletionItem?: (CompletionItem, atom$AutocompleteSuggestion, atom$AutocompleteRequest) => void,
): Map<atom$AutocompleteSuggestion, [CompletionItem, boolean]> {
public completionItemsToSuggestions(
completionItems: CompletionItem[] | CompletionList,
request: AutocompleteRequest,
onDidConvertCompletionItem?: (CompletionItem, AutocompleteSuggestion, AutocompleteRequest) => void,
): Map<AutocompleteSuggestion, [CompletionItem, boolean]> {
return new Map((Array.isArray(completionItems) ? completionItems : completionItems.items || [])
.sort((a, b) => (a.sortText || a.label).localeCompare(b.sortText || b.label))
.map(s => [AutocompleteAdapter.completionItemToSuggestion(s, { }, request, onDidConvertCompletionItem), [s, false]]));
.map<[AutocompleteSuggestion, [CompletionItem, boolean]]>(
(s) => [
AutocompleteAdapter.completionItemToSuggestion(s, { }, request, onDidConvertCompletionItem),
[s, false]]));
}

// Public: Convert a language server protocol CompletionItem to an AutoComplete+ suggestion.
Expand All @@ -206,12 +222,12 @@ export default class AutocompleteAdapter {
// and a {atom$AutocompleteRequest} allowing you to adjust converted items.
//
// Returns the {atom$AutocompleteSuggestion} passed in as suggestion with the conversion applied.
static completionItemToSuggestion(
public static completionItemToSuggestion(
item: CompletionItem,
suggestion: atom$AutocompleteSuggestion,
request: atom$AutocompleteRequest,
onDidConvertCompletionItem?: (CompletionItem, atom$AutocompleteSuggestion, atom$AutocompleteRequest) => void,
): atom$AutocompleteSuggestion {
suggestion: AutocompleteSuggestion,
request: AutocompleteRequest,
onDidConvertCompletionItem?: (CompletionItem, AutocompleteSuggestion, AutocompleteRequest) => void,
): AutocompleteSuggestion {
AutocompleteAdapter.applyCompletionItemToSuggestion(item, suggestion);
AutocompleteAdapter.applyTextEditToSuggestion(item.textEdit, request.editor, suggestion);
AutocompleteAdapter.applySnippetToSuggestion(item, suggestion);
Expand All @@ -228,13 +244,20 @@ export default class AutocompleteAdapter {
// * `suggestion` The {atom$AutocompleteSuggestion} to merge the conversion into.
//
// Returns an {atom$AutocompleteSuggestion} created from the {CompletionItem}.
static applyCompletionItemToSuggestion(item: CompletionItem, suggestion: atom$AutocompleteSuggestion) {
public static applyCompletionItemToSuggestion(item: CompletionItem, suggestion: AutocompleteSuggestion) {
suggestion.text = item.insertText || item.label;
suggestion.displayText = item.label;
suggestion.type = AutocompleteAdapter.completionKindToSuggestionType(item.kind);
suggestion.rightLabel = item.detail;
suggestion.description = item.documentation;
suggestion.descriptionMarkdown = item.documentation;

if (typeof item.documentation === 'object') {
suggestion.descriptionMarkdown = item.documentation.value;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check issues to see if this was done based on an issue filed

suggestion.description = item.documentation.value;
}
else {
suggestion.descriptionMarkdown = item.documentation;
suggestion.description = item.documentation;
}
}

// Public: Applies the textEdit part of a language server protocol CompletionItem to an
Expand All @@ -243,10 +266,10 @@ export default class AutocompleteAdapter {
// * `textEdit` A {TextEdit} from a CompletionItem to apply.
// * `editor` An Atom {TextEditor} used to obtain the necessary text replacement.
// * `suggestion` An {atom$AutocompleteSuggestion} to set the replacementPrefix and text properties of.
static applyTextEditToSuggestion(
textEdit: ?TextEdit,
editor: atom$TextEditor,
suggestion: atom$AutocompleteSuggestion,
public static applyTextEditToSuggestion(
textEdit: TextEdit | null,
editor: TextEditor,
suggestion: AutocompleteSuggestion,
): void {
if (textEdit != null) {
suggestion.replacementPrefix = editor.getTextInBufferRange(Convert.lsRangeToAtomRange(textEdit.range));
Expand All @@ -260,21 +283,20 @@ export default class AutocompleteAdapter {
// * `item` An {CompletionItem} containing the completion items to be merged into.
// * `suggestion` The {atom$AutocompleteSuggestion} to merge the conversion into.
//
static applySnippetToSuggestion(item: CompletionItem, suggestion: atom$AutocompleteSuggestion): void {
public static applySnippetToSuggestion(item: CompletionItem, suggestion: AutocompleteSuggestion): void {
if (item.insertTextFormat === InsertTextFormat.Snippet) {
suggestion.snippet = item.textEdit != null ? item.textEdit.newText : item.insertText;
}
}


// Public: Obtain the textual suggestion type required by AutoComplete+ that
// most closely maps to the numeric completion kind supplies by the language server.
//
// * `kind` A {Number} that represents the suggestion kind to be converted.
//
// Returns a {String} containing the AutoComplete+ suggestion type equivalent
// to the given completion kind.
static completionKindToSuggestionType(kind: ?number): string {
public static completionKindToSuggestionType(kind: number | null): string {
switch (kind) {
case CompletionItemKind.Constant:
return 'constant';
Expand Down
Loading