关键函数入口:
function provideSuggestionItems(model, position, options = CompletionOptions.default, context = { triggerKind: 0 /* Invoke */ }, token = cancellation_1.CancellationToken.None) {
return __awaiter(this, void 0, void 0, function* () {
// const t1 = Date.now();
position = position.clone();
const word = model.getWordAtPosition(position);
const defaultReplaceRange = word ? new range_1.Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn) : range_1.Range.fromPositions(position);
const defaultRange = { replace: defaultReplaceRange, insert: defaultReplaceRange.setEndPosition(position.lineNumber, position.column) };
const result = [];
const disposables = new lifecycle_1.DisposableStore();
let needsClipboard = false;
const onCompletionList = (provider, container) => {
if (!container) {
return;
}
for (let suggestion of container.suggestions) {
if (!options.kindFilter.has(suggestion.kind)) {
// fill in default range when missing
if (!suggestion.range) {
suggestion.range = defaultRange;
}
// fill in default sortText when missing
if (!suggestion.sortText) {
suggestion.sortText = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.name;
}
if (!needsClipboard && suggestion.insertTextRules && suggestion.insertTextRules & 4 /* InsertAsSnippet */) {
needsClipboard = snippetParser_1.SnippetParser.guessNeedsClipboard(suggestion.insertText);
}
result.push(new CompletionItem(position, suggestion, container, provider));
}
}
if (lifecycle_1.isDisposable(container)) {
disposables.add(container);
}
};
// ask for snippets in parallel to asking "real" providers. Only do something if configured to
// do so - no snippet filter, no special-providers-only request
const snippetCompletions = (() => __awaiter(this, void 0, void 0, function* () {
if (!_snippetSuggestSupport || options.kindFilter.has(27 /* Snippet */)) {
return;
}
if (options.providerFilter.size > 0 && !options.providerFilter.has(_snippetSuggestSupport)) {
return;
}
// 关键代码导出提示列表->>>>>>>
const list = yield _snippetSuggestSupport.provideCompletionItems(model, position, context, token);
onCompletionList(_snippetSuggestSupport, list);
}))();
// add suggestions from contributed providers - providers are ordered in groups of
// equal score and once a group produces a result the process stops
// get provider groups, always add snippet suggestion provider
for (let providerGroup of modes.CompletionProviderRegistry.orderedGroups(model)) {
// for each support in the group ask for suggestions
let lenBefore = result.length;
yield Promise.all(providerGroup.map((provider) => __awaiter(this, void 0, void 0, function* () {
let isRet = options.providerFilter.size > 0 && !options.providerFilter.has(provider);
if (isRet) {
return;
}
try {
const list = yield provider.provideCompletionItems(model, position, context, token);
onCompletionList(provider, list);
}
catch (err) {
errors_1.onUnexpectedExternalError(err);
}
})));
if (lenBefore !== result.length || token.isCancellationRequested) {
break;
}
}
yield snippetCompletions;
if (token.isCancellationRequested) {
disposables.dispose();
return Promise.reject(errors_1.canceled());
}
// console.log(`${result.length} items AFTER ${Date.now() - t1}ms`);
// result.reverse();
return new CompletionItemModel(result.sort(getSuggestionComparator(options.snippetSortOrder)), needsClipboard, disposables);
});
}
解析 js/ts 代码模块提取提示信息的地方:
var SuggestAdapter = /** @class */ (function (_super) {
__extends(SuggestAdapter, _super);
function SuggestAdapter() {
return _super !== null && _super.apply(this, arguments) || this;
}
Object.defineProperty(SuggestAdapter.prototype, "triggerCharacters", {
get: function () {
return ['.'];
},
enumerable: false,
configurable: true
});
SuggestAdapter.prototype.provideCompletionItems = function (model, position, _context, token) {
return __awaiter(this, void 0, void 0, function () {
var wordInfo, wordRange, resource, offset, worker, info, suggestions;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
wordInfo = model.getWordUntilPosition(position);
wordRange = new monaco_editor_core_1.Range(position.lineNumber, wordInfo.startColumn, position.lineNumber, wordInfo.endColumn);
resource = model.uri;
offset = model.getOffsetAt(position);
return [4 /*yield*/, this._worker(resource)];
case 1:
worker = _a.sent();
return [4 /*yield*/, worker.getCompletionsAtPosition(resource.toString(), offset)];
case 2:
info = _a.sent();
if (!info || model.isDisposed()) {
return [2 /*return*/];
}
suggestions = info.entries.map(function (entry) {
var _a;
var range = wordRange;
if (entry.replacementSpan) {
var p1 = model.getPositionAt(entry.replacementSpan.start);
var p2 = model.getPositionAt(entry.replacementSpan.start + entry.replacementSpan.length);
range = new monaco_editor_core_1.Range(p1.lineNumber, p1.column, p2.lineNumber, p2.column);
}
var tags = [];
if (((_a = entry.kindModifiers) === null || _a === void 0 ? void 0 : _a.indexOf('deprecated')) !== -1) {
tags.push(monaco_editor_core_1.languages.CompletionItemTag.Deprecated);
}
return {
uri: resource,
position: position,
offset: offset,
range: range,
label: entry.name,
insertText: entry.name,
sortText: entry.sortText,
kind: SuggestAdapter.convertKind(entry.kind),
tags: tags
};
});
return [2 /*return*/, {
suggestions: suggestions
}];
}
});
})
tsWorker 解析核心:
// 获得变量对象成员符号列表
function getCompletionEntryDetails(fileName, position, name, formattingOptions, source, preferences) {
if (preferences === void 0) { preferences = ts.emptyOptions; }
synchronizeHostData();
return ts.Completions.getCompletionEntryDetails(program, log, getValidSourceFile(fileName), position, { name: name, source: source }, host, (formattingOptions && ts.formatting.getFormatContext(formattingOptions, host)), // TODO: GH#18217
preferences, cancellationToken);
}