首页 > 解决方案 > Signature Help Provider is working incorrectly in custom extension

问题描述

We have written some time ago custom extension in VS Code for our internal programming language. We used as starting point built-in PHP extension. It worked fine until one moment (not sure exactly which). Signature Help Provider highlights random parameters. However, in our code, parameter index is correct. I have no idea what is wrong and how SignatureHelp class works.

Here is screenshot how it behaves: enter image description here

And here is code for SignatureHelp (it's mostly untouched old built-in PHP code. Variable activeP contains correct index, its SignatureHelp class doing something wrong):

'use strict';
var vscode_1 = require("vscode");
var myLangGlobals = require("./myLangGlobals");
var markedTextUtil_1 = require("./utils/markedTextUtil");
var _NL = '\n'.charCodeAt(0);
var _TAB = '\t'.charCodeAt(0);
var _WSB = ' '.charCodeAt(0);
var _LBracket = '['.charCodeAt(0);
var _RBracket = ']'.charCodeAt(0);
var _LCurly = '{'.charCodeAt(0);
var _RCurly = '}'.charCodeAt(0);
var _LParent = '('.charCodeAt(0);
var _RParent = ')'.charCodeAt(0);
var _Comma = ','.charCodeAt(0);
var _Quote = '\''.charCodeAt(0);
var _DQuote = '"'.charCodeAt(0);
var _USC = '_'.charCodeAt(0);
var _a = 'a'.charCodeAt(0);
var _z = 'z'.charCodeAt(0);
var _A = 'A'.charCodeAt(0);
var _Z = 'Z'.charCodeAt(0);
var _0 = '0'.charCodeAt(0);
var _9 = '9'.charCodeAt(0);
var BOF = 0;
var BackwardIterator = (function () {
    function BackwardIterator(model, offset, lineNumber) {
        this.lineNumber = lineNumber;
        this.offset = offset;
        this.line = model.lineAt(this.lineNumber).text;
        this.model = model;
    }
    BackwardIterator.prototype.hasNext = function () {
        return this.lineNumber >= 0;
    };
    BackwardIterator.prototype.next = function () {
        if (this.offset < 0) {
            if (this.lineNumber > 0) {
                this.lineNumber--;
                this.line = this.model.lineAt(this.lineNumber).text;
                this.offset = this.line.length - 1;
                return _NL;
            }
            this.lineNumber = -1;
            return BOF;
        }
        var ch = this.line.charCodeAt(this.offset);
        this.offset--;
        return ch;
    };
    return BackwardIterator;
}());
var myLangSignatureHelpProvider = (function () {
    function myLangSignatureHelpProvider() {
    }
    myLangSignatureHelpProvider.prototype.provideSignatureHelp = function (document, position, token) {
        var enable = vscode_1.workspace.getConfiguration('myLang').get('suggest.basic', true);
        if (!enable) {
            return null;
        }
        var iterator = new BackwardIterator(document, position.character - 1, position.line);
        var paramIndex = this.readArguments(iterator);
        if (paramIndex < 0) {
            return null;
        }
        var ident = this.readIdent(iterator);
        if (!ident) {
            return null;
        }
        ident = ident.toLowerCase();


        var entry = myLangGlobals.globalfunctions[ident];
        if (!entry) {
            return null;
        }
        var signature = entry.name;
        var args = entry.args;
        var argCnt = args ? args.length : 0;
        if (entry.type) {
          signature = entry.type + ' ' + signature;
        }
        if (args) {
          signature += '(';
          for (let i = 0; i < argCnt; ++i) {
            if (i) {
              signature += ', ';
            }
            signature += args[i].name;
          }
          signature += ');';
        }
        var activeP = Math.min(paramIndex, Math.max(argCnt - 1, 0));

        var signatureInfo = new vscode_1.SignatureInformation(
          signature,
          new vscode_1.MarkdownString(
            entry.description + '\n\n---\n\n' +
            markedTextUtil_1.textToMarkedString('Version: ' + entry.version) + '\n\n' +
            markedTextUtil_1.textToMarkedString('Documented on: ' + entry.date)
          )
        );
        if (args) {
          for (let i = 0; i < argCnt; ++i) {
            signatureInfo.parameters.push({ label: args[i].name, documentation: args[i].description });
          }
        }
        // TODO: understand, why it highlights incorrect parameter, activeP is correct
        var ret = new vscode_1.SignatureHelp();
        ret.signatures.push(signatureInfo);
        ret.activeSignature = 0;
        ret.activeParameter = activeP;
        return Promise.resolve(ret);
    };
    myLangSignatureHelpProvider.prototype.readArguments = function (iterator) {
        var parentNesting = 0;
        var bracketNesting = 0;
        var curlyNesting = 0;
        var paramCount = 0;
        while (iterator.hasNext()) {
            var ch = iterator.next();
            switch (ch) {
                case _LParent:
                    parentNesting--;
                    if (parentNesting < 0) {
                        return paramCount;
                    }
                    break;
                case _RParent:
                    parentNesting++;
                    break;
                case _LCurly:
                    curlyNesting--;
                    break;
                case _RCurly:
                    curlyNesting++;
                    break;
                case _LBracket:
                    bracketNesting--;
                    break;
                case _RBracket:
                    bracketNesting++;
                    break;
                case _DQuote:
                case _Quote:
                    while (iterator.hasNext() && ch !== iterator.next()) {
                    }
                    break;
                case _Comma:
                    if (!parentNesting && !bracketNesting && !curlyNesting) {
                        paramCount++;
                    }
                    break;
            }
        }
        return -1;
    };
    myLangSignatureHelpProvider.prototype.isIdentPart = function (ch) {
        if (ch === _USC ||
            ch >= _a && ch <= _z ||
            ch >= _A && ch <= _Z ||
            ch >= _0 && ch <= _9 ||
            ch >= 0x80 && ch <= 0xFFFF) {
            return true;
        }
        return false;
    };
    myLangSignatureHelpProvider.prototype.readIdent = function (iterator) {
        var identStarted = false;
        var ident = '';
        while (iterator.hasNext()) {
            var ch = iterator.next();
            if (!identStarted && (ch === _WSB || ch === _TAB || ch === _NL)) {
                continue;
            }
            if (this.isIdentPart(ch)) {
                identStarted = true;
                ident = String.fromCharCode(ch) + ident;
            }
            else if (identStarted) {
                return ident;
            }
        }
        return ident;
    };
    return myLangSignatureHelpProvider;
}());
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = myLangSignatureHelpProvider;

标签: visual-studio-codevscode-extensions

解决方案


您的主要问题在于

signatureInfo.parameters.push({ label: args[i].name, documentation: args[i].description });

由于您有多个具有相同名称的参数,因此仅使用名称作为label. 而不是label: string使用label: [number, number]. 请参阅API 文档中的ParameterInfo

旁注:这个 JavaScript 是从 TypeScript 生成的吗?如果是这样,请提供 TypeScript 源代码。自动生成的代码不那么容易阅读。


推荐阅读