mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-01 09:35:41 -05:00
Merge from vscode 3c6f6af7347d38e87bc6406024e8dcf9e9bce229 (#8962)
* Merge from vscode 3c6f6af7347d38e87bc6406024e8dcf9e9bce229 * skip failing tests * update mac build image
This commit is contained in:
committed by
Karl Burtram
parent
0eaee18dc4
commit
fefe1454de
@@ -30,6 +30,7 @@ import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { encodeSemanticTokensDto } from 'vs/workbench/api/common/shared/semanticTokens';
|
||||
import { IdGenerator } from 'vs/base/common/idGenerator';
|
||||
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
|
||||
import { Cache } from './cache';
|
||||
|
||||
// --- adapter
|
||||
|
||||
@@ -818,20 +819,20 @@ class SuggestAdapter {
|
||||
const disposables = new DisposableStore();
|
||||
this._disposables.set(pid, disposables);
|
||||
|
||||
const completions: extHostProtocol.ISuggestDataDto[] = [];
|
||||
const result: extHostProtocol.ISuggestResultDto = {
|
||||
x: pid,
|
||||
b: [],
|
||||
a: { replace: typeConvert.Range.from(replaceRange), insert: typeConvert.Range.from(insertRange) },
|
||||
c: list.isIncomplete || undefined,
|
||||
d: list.isDetailsResolved || undefined
|
||||
[extHostProtocol.ISuggestResultDtoField.completions]: completions,
|
||||
[extHostProtocol.ISuggestResultDtoField.defaultRanges]: { replace: typeConvert.Range.from(replaceRange), insert: typeConvert.Range.from(insertRange) },
|
||||
[extHostProtocol.ISuggestResultDtoField.isIncomplete]: list.isIncomplete || undefined
|
||||
};
|
||||
|
||||
for (let i = 0; i < list.items.length; i++) {
|
||||
const suggestion = this._convertCompletionItem(list.items[i], pos, [pid, i]);
|
||||
// check for bad completion item
|
||||
// for the converter did warn
|
||||
if (suggestion) {
|
||||
result.b.push(suggestion);
|
||||
const item = list.items[i];
|
||||
// check for bad completion item first
|
||||
if (this._validateCompletionItem(item, pos)) {
|
||||
const dto = this._convertCompletionItem(item, [pid, i], insertRange, replaceRange);
|
||||
completions.push(dto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -850,12 +851,13 @@ class SuggestAdapter {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
const pos = typeConvert.Position.to(position);
|
||||
const _mustNotChange = SuggestAdapter._mustNotChangeHash(item);
|
||||
const _mayNotChange = SuggestAdapter._mayNotChangeHash(item);
|
||||
|
||||
return asPromise(() => this._provider.resolveCompletionItem!(item, token)).then(resolvedItem => {
|
||||
|
||||
if (!resolvedItem) {
|
||||
if (!resolvedItem || !this._validateCompletionItem(resolvedItem, pos)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -874,19 +876,18 @@ class SuggestAdapter {
|
||||
let _mustNotChangeIndex = !this._didWarnMust && SuggestAdapter._mustNotChangeDiff(_mustNotChange, resolvedItem);
|
||||
if (typeof _mustNotChangeIndex === 'string') {
|
||||
this._logService.warn(`[${this._extension.identifier.value}] INVALID result from 'resolveCompletionItem', extension MUST NOT change any of: label, sortText, filterText, insertText, or textEdit`);
|
||||
this._telemetry.$publicLog2<BlameExtension, BlameExtensionMeta>('resolveCompletionItem/invalid', { extensionId: this._extension.identifier.value, kind: 'must', index: _mustNotChangeIndex });
|
||||
this._telemetry.$publicLog2<BlameExtension, BlameExtensionMeta>('badresolvecompletion', { extensionId: this._extension.identifier.value, kind: 'must', index: _mustNotChangeIndex });
|
||||
this._didWarnMust = true;
|
||||
}
|
||||
|
||||
let _mayNotChangeIndex = !this._didWarnShould && SuggestAdapter._mayNotChangeDiff(_mayNotChange, resolvedItem);
|
||||
if (typeof _mayNotChangeIndex === 'string') {
|
||||
this._logService.info(`[${this._extension.identifier.value}] UNSAVE result from 'resolveCompletionItem', extension SHOULD NOT change any of: additionalTextEdits, or command`);
|
||||
this._telemetry.$publicLog2<BlameExtension, BlameExtensionMeta>('resolveCompletionItem/invalid', { extensionId: this._extension.identifier.value, kind: 'should', index: _mayNotChangeIndex });
|
||||
this._telemetry.$publicLog2<BlameExtension, BlameExtensionMeta>('badresolvecompletion', { extensionId: this._extension.identifier.value, kind: 'should', index: _mayNotChangeIndex });
|
||||
this._didWarnShould = true;
|
||||
}
|
||||
|
||||
const pos = typeConvert.Position.to(position);
|
||||
return this._convertCompletionItem(resolvedItem, pos, id);
|
||||
return this._convertCompletionItem(resolvedItem, id);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -896,11 +897,7 @@ class SuggestAdapter {
|
||||
this._cache.delete(id);
|
||||
}
|
||||
|
||||
private _convertCompletionItem(item: vscode.CompletionItem, position: vscode.Position, id: extHostProtocol.ChainedCacheId): extHostProtocol.ISuggestDataDto | undefined {
|
||||
if (typeof item.label !== 'string' || item.label.length === 0) {
|
||||
this._logService.warn('INVALID text edit -> must have at least a label');
|
||||
return undefined;
|
||||
}
|
||||
private _convertCompletionItem(item: vscode.CompletionItem, id: extHostProtocol.ChainedCacheId, defaultInsertRange?: vscode.Range, defaultReplaceRange?: vscode.Range): extHostProtocol.ISuggestDataDto {
|
||||
|
||||
const disposables = this._disposables.get(id[0]);
|
||||
if (!disposables) {
|
||||
@@ -912,6 +909,7 @@ class SuggestAdapter {
|
||||
x: id,
|
||||
//
|
||||
[extHostProtocol.ISuggestDataDtoField.label]: item.label,
|
||||
[extHostProtocol.ISuggestDataDtoField.label2]: item.label2,
|
||||
[extHostProtocol.ISuggestDataDtoField.kind]: typeConvert.CompletionItemKind.from(item.kind),
|
||||
[extHostProtocol.ISuggestDataDtoField.kindModifier]: item.tags && item.tags.map(typeConvert.CompletionItemTag.from),
|
||||
[extHostProtocol.ISuggestDataDtoField.detail]: item.detail,
|
||||
@@ -927,9 +925,7 @@ class SuggestAdapter {
|
||||
|
||||
// 'insertText'-logic
|
||||
if (item.textEdit) {
|
||||
this._apiDeprecation.report('CompletionItem.textEdit', this._extension,
|
||||
`Use 'CompletionItem.insertText' and 'CompletionItem.range' instead.`);
|
||||
|
||||
this._apiDeprecation.report('CompletionItem.textEdit', this._extension, `Use 'CompletionItem.insertText' and 'CompletionItem.range' instead.`);
|
||||
result[extHostProtocol.ISuggestDataDtoField.insertText] = item.textEdit.newText;
|
||||
|
||||
} else if (typeof item.insertText === 'string') {
|
||||
@@ -948,36 +944,44 @@ class SuggestAdapter {
|
||||
range = item.range;
|
||||
}
|
||||
|
||||
if (range) {
|
||||
if (Range.isRange(range)) {
|
||||
if (!SuggestAdapter._isValidRangeForCompletion(range, position)) {
|
||||
this._logService.trace('INVALID range -> must be single line and on the same line');
|
||||
return undefined;
|
||||
}
|
||||
result[extHostProtocol.ISuggestDataDtoField.range] = typeConvert.Range.from(range);
|
||||
if (Range.isRange(range)) {
|
||||
// "old" range
|
||||
result[extHostProtocol.ISuggestDataDtoField.range] = typeConvert.Range.from(range);
|
||||
|
||||
} else {
|
||||
if (
|
||||
!SuggestAdapter._isValidRangeForCompletion(range.inserting, position)
|
||||
|| !SuggestAdapter._isValidRangeForCompletion(range.replacing, position)
|
||||
|| !range.inserting.start.isEqual(range.replacing.start)
|
||||
|| !range.replacing.contains(range.inserting)
|
||||
) {
|
||||
this._logService.trace('INVALID range -> must be single line, on the same line, insert range must be a prefix of replace range');
|
||||
return undefined;
|
||||
}
|
||||
result[extHostProtocol.ISuggestDataDtoField.range] = {
|
||||
insert: typeConvert.Range.from(range.inserting),
|
||||
replace: typeConvert.Range.from(range.replacing)
|
||||
};
|
||||
}
|
||||
} else if (range && !defaultInsertRange?.isEqual(range.inserting) && !defaultReplaceRange?.isEqual(range.replacing)) {
|
||||
// ONLY send range when it's different from the default ranges (safe bandwidth)
|
||||
result[extHostProtocol.ISuggestDataDtoField.range] = {
|
||||
insert: typeConvert.Range.from(range.inserting),
|
||||
replace: typeConvert.Range.from(range.replacing)
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static _isValidRangeForCompletion(range: vscode.Range, position: vscode.Position): boolean {
|
||||
return range.isSingleLine || range.start.line === position.line;
|
||||
private _validateCompletionItem(item: vscode.CompletionItem, position: vscode.Position): boolean {
|
||||
if (typeof item.label !== 'string' || item.label.length === 0) {
|
||||
this._logService.warn('INVALID text edit -> must have at least a label');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Range.isRange(item.range)) {
|
||||
if (!item.range.isSingleLine || item.range.start.line !== position.line) {
|
||||
this._logService.trace('INVALID range -> must be single line and on the same line');
|
||||
return false;
|
||||
}
|
||||
|
||||
} else if (item.range) {
|
||||
if (!item.range.inserting.isSingleLine || item.range.inserting.start.line !== position.line
|
||||
|| !item.range.replacing.isSingleLine || item.range.replacing.start.line !== position.line
|
||||
|| !item.range.inserting.start.isEqual(item.range.replacing.start)
|
||||
|| !item.range.replacing.contains(item.range.inserting)
|
||||
) {
|
||||
this._logService.trace('INVALID range -> must be single line, on the same line, insert range must be a prefix of replace range');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static _mustNotChangeHash(item: vscode.CompletionItem) {
|
||||
@@ -1064,40 +1068,6 @@ class SignatureHelpAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
class Cache<T> {
|
||||
private static readonly enableDebugLogging = false;
|
||||
|
||||
private readonly _data = new Map<number, readonly T[]>();
|
||||
private _idPool = 1;
|
||||
|
||||
constructor(
|
||||
private readonly id: string
|
||||
) { }
|
||||
|
||||
add(item: readonly T[]): number {
|
||||
const id = this._idPool++;
|
||||
this._data.set(id, item);
|
||||
this.logDebugInfo();
|
||||
return id;
|
||||
}
|
||||
|
||||
get(pid: number, id: number): T | undefined {
|
||||
return this._data.has(pid) ? this._data.get(pid)![id] : undefined;
|
||||
}
|
||||
|
||||
delete(id: number) {
|
||||
this._data.delete(id);
|
||||
this.logDebugInfo();
|
||||
}
|
||||
|
||||
private logDebugInfo() {
|
||||
if (!Cache.enableDebugLogging) {
|
||||
return;
|
||||
}
|
||||
console.log(`${this.id} cache size — ${this._data.size}`);
|
||||
}
|
||||
}
|
||||
|
||||
class LinkProviderAdapter {
|
||||
|
||||
private _cache = new Cache<vscode.DocumentLink>('DocumentLink');
|
||||
|
||||
Reference in New Issue
Block a user