From d67fea7b0d2219e510c5dab3b52b3ad53b309cd7 Mon Sep 17 00:00:00 2001 From: JohnnyMorganz Date: Sun, 28 Jun 2020 15:59:43 +0100 Subject: [PATCH] Create a new Completion Provider --- src/completionProvider.ts | 315 ++++++++++++++++++++++++++++++++++++++ src/extension.ts | 7 +- src/itemStruct.ts | 111 -------------- src/services.ts | 230 ---------------------------- src/signatureProvider.ts | 11 +- 5 files changed, 322 insertions(+), 352 deletions(-) create mode 100644 src/completionProvider.ts delete mode 100644 src/itemStruct.ts delete mode 100644 src/services.ts diff --git a/src/completionProvider.ts b/src/completionProvider.ts new file mode 100644 index 0000000..5fa5cb1 --- /dev/null +++ b/src/completionProvider.ts @@ -0,0 +1,315 @@ +// Roblox Completion Provider +// Provides completion items for Classes and DataTypes, as well as properties with custom DataTypes + +import * as vscode from "vscode" +import { getAutocompleteDump } from "./autocompleteDump" +import { ApiClass, ApiMember, ApiPropertySecurity, getApiDump, UNCREATABLE_TAGS } from "./dump" +import { createDocumentationString, createParameterLabel, createStructParameterLabel, inferType } from "./utils" + +const UNSCRIPTABLE_TAGS: Set = new Set([ + "Deprecated", + "Hidden", + "NotBrowsable", + "NotScriptable", +]) + +const IMPORT_PATTERN = /^local \w+ = game:GetService\("\w+"\)\s*$/ + +const getAllowedClassMembers = (member: ApiMember) => { + const tags = member.Tags + if (tags !== undefined) { + for (const tag of tags) { + if (UNSCRIPTABLE_TAGS.has(tag)) { + return false + } + } + } + return true +} + +const getClassCompletionItemKind = (type: "Function" | "Callback" | "Event" | "Property") => { + switch (type) { + case "Function": + return vscode.CompletionItemKind.Method + case "Property": + return vscode.CompletionItemKind.Field + case "Callback": + return vscode.CompletionItemKind.Constructor + case "Event": + return vscode.CompletionItemKind.Event + } +} + +const getClassCompletionItemDetail = (member: ApiMember, service: ApiClass) => { + switch (member.MemberType) { + case "Function": + return `(method) ${service.Name}:${member.Name}(${member.Parameters.map(parameter => createParameterLabel(parameter)).join(", ")}): ${member.ReturnType ? member.ReturnType.Name : "unknown"}` + case "Property": + return `(property) ${service.Name}.${member.Name}: ${member.ValueType ? member.ValueType.Name : "unknown"}` + case "Callback": + return `(callback) ${service.Name}.${member.Name} = function (${member.Parameters.map(parameter => `${parameter.Name}: ${parameter.Type ? parameter.Type.Name : "unknown"}`).join(", ")})` + case "Event": + return `(event) ${service.Name}.${member.Name}(${member.Parameters.map(parameter => `${parameter.Name}: ${parameter.Type ? parameter.Type.Name : "unknown"}`).join(", ")})` + } +} + +export class RobloxCompletionProvider implements vscode.CompletionItemProvider { + private classCompletion: Promise>> + private structCompletion: Promise>> + private itemStructNames: Promise + + constructor() { + this.classCompletion = (async () => { + const dotCompletion = new Map() + const colonCompletion = new Map() + + const classes = (await getApiDump()).Classes + for (const klass of classes) { + const dotOperatorItems: vscode.CompletionItem[] = [] + const colonOperatorItems: vscode.CompletionItem[] = [] + + for (const member of klass.Members.filter(getAllowedClassMembers)) { + if (member.MemberType === "Property") { + const security = member.Security as ApiPropertySecurity + if (security.Read !== "None" && security.Write !== "None") { + continue + } + } else if (member.Security !== "None") { + continue + } + + const completionItem = new vscode.CompletionItem( + member.Name, + getClassCompletionItemKind(member.MemberType) + ) + completionItem.detail = getClassCompletionItemDetail(member, klass) + completionItem.documentation = createDocumentationString(member, member.MemberType, klass.Name) + + if (member.MemberType === "Function") { + colonOperatorItems.push(completionItem) + } else { + dotOperatorItems.push(completionItem) + } + } + + dotCompletion.set(klass.Name, dotOperatorItems) + colonCompletion.set(klass.Name, colonOperatorItems) + } + + return [ dotCompletion, colonCompletion ] + })() + + this.structCompletion = (async () => { + const dotCompletion = new Map() + const colonCompletion = new Map() + + const autocompleteDump = await getAutocompleteDump() + + for (const itemStruct of autocompleteDump.ItemStruct) { + const dotOperatorItems: vscode.CompletionItem[] = [] + const colonOperatorItems: vscode.CompletionItem[] = [] + + itemStruct.properties.map(property => { + const item = new vscode.CompletionItem( + property.name, + vscode.CompletionItemKind.Field, + ) + item.detail = `(property) ${itemStruct.name}.${property.name}: ${property.type}` + item.documentation = new vscode.MarkdownString(`${property.description ? property.description + "\n\n" : ""}[Developer Reference](https://developer.roblox.com/en-us/api-reference/datatype/${itemStruct.name})`) + return item + }).forEach(item => dotOperatorItems.push(item)) + + const completedFuncs: { [name: string]: boolean } = {} + + itemStruct.functions.map(func => { + const item = new vscode.CompletionItem( + func.name, + func.static ? vscode.CompletionItemKind.Function : vscode.CompletionItemKind.Method, + ) + item.detail = `(${func.static ? "function" : "method"}) ${itemStruct.name}.${func.name}(${func.parameters.map(parameter => createStructParameterLabel(parameter)).join(", ")}): ${func.returns.length > 0 ? func.returns.map((ret) => ret.type).join(", ") : "unknown"}` + item.documentation = new vscode.MarkdownString(`${func.description ? func.description + "\n\n" : ""}[Developer Reference](https://developer.roblox.com/en-us/api-reference/datatype/${itemStruct.name})`) + return { item, func } + }).forEach( + ({ item, func }) => { + // Merge together overloaded functions + if (completedFuncs[func.name] === undefined) { + const count = itemStruct.functions.filter(f => f.name === func.name).length + if (count > 1) { + item.detail += ` (+${count - 1} overload${count === 1 ? "" : "s"})` + } + + func.static ? dotOperatorItems.push(item) : colonOperatorItems.push(item) + completedFuncs[func.name] = true + } + }) + + dotCompletion.set(itemStruct.name, dotOperatorItems) + colonCompletion.set(itemStruct.name, colonOperatorItems) + } + + return [ dotCompletion, colonCompletion ] + })() + + this.itemStructNames = (async () => { + const autocompleteDump = await getAutocompleteDump() + return autocompleteDump.ItemStruct.filter( + (itemStruct) => { + return itemStruct.functions.filter( + func => func.static, + ).length > 0 || itemStruct.properties.filter((property) => property.static).length > 0 + }, + ).map( + (itemStruct) => new vscode.CompletionItem(itemStruct.name, vscode.CompletionItemKind.Class), + ) + })() + } + + public async provideCompletionItems( + document: vscode.TextDocument, + position: vscode.Position, + token: vscode.CancellationToken, + context: vscode.CompletionContext, + ) { + const codeAtLine = document.lineAt(position.line).text.substr(0, position.character) + const codeString = codeAtLine.match(/([\w.:()'"]+)([.:])([\w()"']*)$/) + + console.log(codeAtLine, codeString) + + if (codeString !== null) { + const indexString = codeString[1] + const operator = codeString[2] + const extra = codeString[3] + + const types = await inferType(document, position, indexString) + + const mainType = types[types.length - 1] + if (mainType === undefined) { + return null + } + + if (mainType.Category === "Class") { + const apiDump = await getApiDump() + const service = apiDump.Classes.find(klass => klass.Name === mainType.Name) + if (service !== undefined && service.Tags !== undefined && service.Tags.includes("Service")) { + // Provide auto import if it is a service and is not already present + const documentText = document.getText() + + if (!documentText.match(new RegExp(`^local ${service.Name}\\s*=\\s*`, "m"))) { + const insertText = `local ${service.Name} = game:GetService("${service.Name}")\n` + const lines = documentText.split(/\n\r?/) + + const firstImport = lines.findIndex(line => line.match(IMPORT_PATTERN)) + let lineNumber = Math.max(firstImport, 0) + + while (lineNumber < lines.length) { + if ( + !lines[lineNumber].match(IMPORT_PATTERN) + || lines[lineNumber] > insertText + ) { + break + } + lineNumber++ + } + + const item = new vscode.CompletionItem( + service.Name, + vscode.CompletionItemKind.Class, + ) + + item.additionalTextEdits = [ + vscode.TextEdit.insert( + new vscode.Position(lineNumber, 0), + insertText + (firstImport === -1 ? "\n" : ""), + ), + ] + + if (operator !== "") { + item.command = { command: "editor.action.triggerSuggest", title: "Re-trigger completions" } + } + + item.detail = `Auto-import ${service.Name}` + item.documentation = new vscode.MarkdownString(`${service.Description ? service.Description + "\n\n" : ""}[Developer Reference](https://developer.roblox.com/en-us/api-reference/class/${service.Name})`) + item.insertText = operator ? "" : service.Name + item.preselect = true + + return [item] + } + } + + const [ dotCompletion, colonCompletion ] = await this.classCompletion + if (operator === ".") { + return dotCompletion.get(mainType.Name) + } else if (operator === ":") { + return colonCompletion.get(mainType.Name) + } + } else if (mainType.Category === "DataType") { + const [ dotCompletion, colonCompletion ] = await this.structCompletion + + // Provide support for Instance.new() etc. + if (extra !== null) { + const extraMatch = extra.match(/([\w.:]+)\(([\w"',\s.:()-]*)?$/) + if (extraMatch !== null) { + const functionName = extraMatch[1] + const parameters = extraMatch[2] + + const itemStruct = (await getAutocompleteDump()).ItemStruct.find(struct => struct.name === mainType.Name) + const itemStructFunc = itemStruct?.functions.find(func => func.name === functionName) + const parameter = itemStructFunc?.parameters[parameters.split(",").length - 1] + + if (parameter !== undefined && parameter.constraint !== undefined) { + const constraintSplit = parameter.constraint.split(":") + const objectType = constraintSplit[0] + const constraint = constraintSplit[1] || "any" + + const apiDump = await getApiDump() + const options = apiDump.Classes.filter(klass => { + if (objectType === "Instance") { + if (constraint === "any") { + return true + } else if (constraint === "isScriptCreatable") { + const tags = klass.Tags + if (tags) { + for (const tag of tags) { + if (UNCREATABLE_TAGS.has(tag)) { + return false + } + } + } + return true + } + } + return false + }).map(klass => { + const completionItem = new vscode.CompletionItem( + klass.Name, + vscode.CompletionItemKind.Constant, + ) + + completionItem.documentation = new vscode.MarkdownString(`${klass.Description ? klass.Description + "\n\n" : ""}[Developer Reference](https://developer.roblox.com/en-us/api-reference/class/${klass.Name})`) + return completionItem + }) + + return options + } + return null + } + } + + if (operator === ".") { + if (mainType.Static === true) { + return dotCompletion.get(mainType.Name)?.filter(item => item.kind === vscode.CompletionItemKind.Function) + } else { + return dotCompletion.get(mainType.Name)?.filter(item => item.kind !== vscode.CompletionItemKind.Function) + } + } else if (operator === ":" && mainType.Static === false) { + return colonCompletion.get(mainType.Name) + } + } + + return null + } else { + // Provide completion items for creatable DataTypes + return this.itemStructNames + } + } +} \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index 67513d9..0dad436 100755 --- a/src/extension.ts +++ b/src/extension.ts @@ -3,13 +3,11 @@ import * as vscode from "vscode" import { RobloxColorProvider } from "./color" import { Companion } from "./companion" +import { RobloxCompletionProvider } from "./completionProvider" import { EnumCompletionProvider } from "./enum" -import { ItemStructCompletionProvider } from "./itemStruct" import { LuaLibraryCompletionProvider } from "./luaLibrary" import { RojoHandler } from "./rojo" -import { ServiceCompletionProvider } from "./services" import { RobloxSignatureProvider } from "./signatureProvider" -import { inferType } from "./utils" const SELECTOR = { scheme: "file", language: "lua" } export async function activate(context: vscode.ExtensionContext) { @@ -42,8 +40,7 @@ export async function activate(context: vscode.ExtensionContext) { context.subscriptions.push(vscode.languages.registerColorProvider(SELECTOR, new RobloxColorProvider())) context.subscriptions.push(vscode.languages.registerCompletionItemProvider(SELECTOR, new EnumCompletionProvider(), ".")) - context.subscriptions.push(vscode.languages.registerCompletionItemProvider(SELECTOR, new ServiceCompletionProvider(), ".", ":")) context.subscriptions.push(vscode.languages.registerCompletionItemProvider(SELECTOR, new LuaLibraryCompletionProvider(), ".")) - context.subscriptions.push(vscode.languages.registerCompletionItemProvider(SELECTOR, new ItemStructCompletionProvider(), ".")) context.subscriptions.push(vscode.languages.registerSignatureHelpProvider(SELECTOR, new RobloxSignatureProvider(), "(", ",")) + context.subscriptions.push(vscode.languages.registerCompletionItemProvider(SELECTOR, new RobloxCompletionProvider(), ".", ":", "\"", "'")) } diff --git a/src/itemStruct.ts b/src/itemStruct.ts deleted file mode 100644 index b1050e4..0000000 --- a/src/itemStruct.ts +++ /dev/null @@ -1,111 +0,0 @@ -import * as vscode from "vscode" -import { getAutocompleteDump } from "./autocompleteDump" -import { getApiDump, UNCREATABLE_TAGS } from "./dump" - -export class ItemStructCompletionProvider implements vscode.CompletionItemProvider { - itemStructNames: Promise - itemStructs: Promise<{ [name: string]: vscode.CompletionItem[] }> - - constructor() { - this.itemStructs = (async () => { - const autocompleteDump = await getAutocompleteDump() - const apiDump = await getApiDump() - const itemStructs: { [name: string]: vscode.CompletionItem[] } = {} - for (const itemStruct of autocompleteDump.ItemStruct) { - itemStructs[itemStruct.name] = [ - ...itemStruct.properties.filter(property => property.static).map(property => { - const item = new vscode.CompletionItem(property.name, vscode.CompletionItemKind.Field) - item.detail = `(property) ${itemStruct.name}.${property.name}: ${property.type}` - item.documentation = new vscode.MarkdownString(`${property.description ? property.description + "\n\n" : ""}[Developer Reference](https://developer.roblox.com/en-us/api-reference/datatype/${itemStruct.name})`) - return item - }), - ...itemStruct.functions.filter(func => func.static).map(func => { - const insertText = new vscode.SnippetString(`${func.name}(`) - - const params = [] - for (const paramIndex in func.parameters) { - if (func.parameters[paramIndex]) { - const param = func.parameters[paramIndex] - const paramText = `${param.name}${param.optional ? "?" : ""}: ${param.type || "unknown"}` - params.push(paramText) - - // Create a snippet if the parameters are definable (eg. Instance.new()) - let paramInsertText = null - if (param.constraint) { - const constraintSplit = param.constraint.split(":") - const objectType = constraintSplit[0] - const constraint = constraintSplit[1] || "any" - - paramInsertText = apiDump.Classes.filter(klass => { - if (objectType === "Instance") { - if (constraint === "any") { - return true - } else if (constraint === "isScriptCreatable") { - const tags = klass.Tags - if (tags) { - for (const tag of tags) { - if (UNCREATABLE_TAGS.has(tag)) { - return false - } - } - } - return true - } - } - return false - }).map(klass => klass.Name).sort().join(",") - } - - if (paramInsertText) { - insertText.value += `"\${${paramIndex + 1}|${paramInsertText}|}"` - } - } - } - // End parantheses and set the cursor inside or outside the parens depending on param count - insertText.value += `${params.length > 0 ? `$0)` : `)$0`}` - - const item = new vscode.CompletionItem( - func.name, - vscode.CompletionItemKind.Function, - ) - item.insertText = insertText - item.detail = `(function) ${itemStruct.name}.${func.name}(${params.join(", ")}): ${func.returns.length > 0 ? func.returns.map((ret) => ret.type).join(", ") : "unknown"}` - item.documentation = new vscode.MarkdownString(`${func.description ? func.description + "\n\n" : ""}[Developer Reference](https://developer.roblox.com/en-us/api-reference/datatype/${itemStruct.name})`) - return item - }), - ] - } - - return itemStructs - })() - - this.itemStructNames = (async () => { - const autocompleteDump = await getAutocompleteDump() - return autocompleteDump.ItemStruct.filter( - (itemStruct) => { - return itemStruct.functions.filter( - func => func.static, - ).length > 0 || itemStruct.properties.filter((property) => property.static).length > 0 - }, - ).map( - (itemStruct) => new vscode.CompletionItem(itemStruct.name, vscode.CompletionItemKind.Class), - ) - })() - } - - async provideCompletionItems(document: vscode.TextDocument, position: vscode.Position) { - const textSplit = document.lineAt(position.line).text.substr(0, position.character).split(/[^\w\.]+/) - const text = textSplit[textSplit.length - 1] - - if (text !== undefined) { - const tokens = text.split(".") - if (tokens.length === 1) { - return this.itemStructNames - } else if (tokens.length === 2) { - const library = tokens[0] - const items = (await this.itemStructs)[library] - return items || [] - } - } - } -} diff --git a/src/services.ts b/src/services.ts deleted file mode 100644 index 86fd7f2..0000000 --- a/src/services.ts +++ /dev/null @@ -1,230 +0,0 @@ -// Service member auto complete - -import * as vscode from "vscode" -import { ApiClass, ApiPropertySecurity, getApiDump } from "./dump" - -const UNSCRIPTABLE_TAGS: Set = new Set([ - "Deprecated", - "Hidden", - "NotBrowsable", - "NotScriptable", -]) - -const IMPORT_PATTERN = /^local \w+ = game:GetService\("\w+"\)\s*$/ - -export class ServiceCompletionProvider implements vscode.CompletionItemProvider { - serviceMembers: Promise> - servicesCompletion: Promise - - constructor() { - this.serviceMembers = getApiDump().then(dump => { - const output = new Map() - - for (const klass of dump.Classes) { - const klassData = { - Description: klass.Description, - Members: klass.Members.filter((member) => { - const tags = member.Tags - if (tags !== undefined) { - for (const tag of tags) { - if (UNSCRIPTABLE_TAGS.has(tag)) { - return false - } - } - } - return true - }), - MemoryCategory: klass.MemoryCategory, - Name: klass.Name, - Superclass: klass.Superclass, - Tags: klass.Tags, - } - output.set(klass.Name, klassData) - } - - return output - }) - - this.servicesCompletion = getApiDump().then(dump => { - const output: vscode.CompletionItem[] = [] - - for (const klass of dump.Classes) { - if (klass.Tags !== undefined && klass.Tags.includes("Service")) { - const completionItem = new vscode.CompletionItem(klass.Name, vscode.CompletionItemKind.Class) - - completionItem.detail = `(service) ${klass.Name}` - completionItem.documentation = new vscode.MarkdownString(`[Developer Reference](https://developer.roblox.com/en-us/api-reference/class/${klass.Name})`) - - output.push(completionItem) - } - } - - return output - }) - } - - async createCompletionItems( - service: ApiClass, - operator: string, - ): Promise { - let completionItems: vscode.CompletionItem[] = [] - - for (const member of service.Members) { - if (member.MemberType === "Property") { - const security = member.Security as ApiPropertySecurity - if (security.Read !== "None" && security.Write !== "None") { - continue - } - } else if (member.Security !== "None") { - continue - } - - if (operator === ":") { - if (member.MemberType === "Function") { - const params = [] - - for (const param of member.Parameters) { - const paramText = `${param.Name}${param.Default ? "?" : ""}: ${param.Type ? param.Type.Name : "unknown"}${param.Default ? ` = ${param.Default}` : ""}` - params.push(paramText) - } - - const completionItem = new vscode.CompletionItem( - member.Name, - vscode.CompletionItemKind.Method, - ) - - completionItem.detail = `(function) ${service.Name}:${member.Name}(${params.join(", ")}): ${member.ReturnType ? member.ReturnType.Name : "unknown"}` - completionItem.documentation = new vscode.MarkdownString(`[Developer Reference](https://developer.roblox.com/en-us/api-reference/function/${service.Name}/${member.Name})`) - completionItem.insertText = new vscode.SnippetString(`${member.Name}(${params.length > 0 ? "$0)" : ")$0"}`) - - completionItems.push(completionItem) - } - } else if (operator === ".") { - switch (member.MemberType) { - case "Callback": { - const params = [] - - for (const param of member.Parameters) { - const paramText = `${param.Name}: ${param.Type ? param.Type.Name : "unknown"}` - params.push(paramText) - } - - const completionItem = new vscode.CompletionItem( - member.Name, - vscode.CompletionItemKind.Constructor, - ) - completionItem.detail = `(callback) ${service.Name}.${member.Name} = function (${params.join(", ")})` - completionItem.documentation = new vscode.MarkdownString(`[Developer Reference](https://developer.roblox.com/en-us/api-reference/callback/${service.Name}/${member.Name})`) - - completionItems.push(completionItem) - break - } - case "Event": { - const params = [] - - for (const param of member.Parameters) { - const paramText = `${param.Name}: ${param.Type ? param.Type.Name : "unknown"}` - params.push(paramText) - } - - const completionItem = new vscode.CompletionItem( - member.Name, - vscode.CompletionItemKind.Event, - ) - completionItem.detail = `(event) ${service.Name}.${member.Name}(${params.join(", ")})` - completionItem.documentation = new vscode.MarkdownString(`[Developer Reference](https://developer.roblox.com/en-us/api-reference/property/${service.Name}/${member.Name})`) - - completionItems.push(completionItem) - break - } - case "Property": { - const completionItem = new vscode.CompletionItem( - member.Name, - vscode.CompletionItemKind.Field, - ) - completionItem.detail = `(property) ${service.Name}.${member.Name}: ${member.ValueType ? member.ValueType.Name : "unknown"}` - completionItem.documentation = new vscode.MarkdownString(`[Developer Reference](https://developer.roblox.com/en-us/api-reference/event/${service.Name}/${member.Name})`) - completionItems.push(completionItem) - break - } - } - } - } - - if (service.Superclass) { - const klass = (await this.serviceMembers).get(service.Superclass) - if (klass) { - const inheritedMembers = await this.createCompletionItems(klass, operator) - for (const completionItem of inheritedMembers) { - if (completionItem.documentation) { - (completionItem.documentation as vscode.MarkdownString).value = `Inherited from ${service.Superclass}\n\n${(completionItem.documentation as vscode.MarkdownString).value}` - } - } - completionItems = completionItems.concat(inheritedMembers) - } - } - - return completionItems - } - - public async provideCompletionItems(document: vscode.TextDocument, position: vscode.Position) { - const serviceMatch = document.lineAt(position.line).text.substr(0, position.character).match(/(\w+)([:.]?)\w*$/) - - if (serviceMatch !== null) { - const serviceName = serviceMatch[1] - const operator = serviceMatch[2] - - const service = (await this.serviceMembers).get(serviceName) - - if (service !== undefined && service.Tags !== undefined && service.Tags.includes("Service")) { - const documentText = document.getText() - - if (!documentText.match(new RegExp(`^local ${serviceName}\\s*=\\s*`, "m"))) { - const insertText = `local ${serviceName} = game:GetService("${serviceName}")\n` - const lines = documentText.split(/\n\r?/) - - const firstImport = lines.findIndex(line => line.match(IMPORT_PATTERN)) - let lineNumber = Math.max(firstImport, 0) - - while (lineNumber < lines.length) { - if ( - !lines[lineNumber].match(IMPORT_PATTERN) - || lines[lineNumber] > insertText - ) { - break - } - lineNumber++ - } - - const item = new vscode.CompletionItem( - serviceName, - vscode.CompletionItemKind.Class, - ) - - item.additionalTextEdits = [ - vscode.TextEdit.insert( - new vscode.Position(lineNumber, 0), - insertText + (firstImport === -1 ? "\n" : ""), - ), - ] - - if (operator !== "") { - item.command = { command: "editor.action.triggerSuggest", title: "Re-trigger completions" } - } - - item.detail = "Auto-import service" - item.insertText = operator ? "" : serviceName - item.preselect = true - - return [item] - } - - const completionItems = await this.createCompletionItems(service, operator) - - return completionItems - } - } - - return [] - } -} diff --git a/src/signatureProvider.ts b/src/signatureProvider.ts index c97884b..1c1a194 100644 --- a/src/signatureProvider.ts +++ b/src/signatureProvider.ts @@ -27,7 +27,7 @@ const parameterizeSignature = ( for (const parameterIndex in parameters) { if (parameters[parameterIndex]) { const parameter = parameters[parameterIndex] - const parameterLabel = + const parameterLabel = isApiParameter(parameter) ? createParameterLabel(parameter) : createStructParameterLabel(parameter) signature.parameters.push( new vscode.ParameterInformation( @@ -75,7 +75,7 @@ export class RobloxSignatureProvider implements vscode.SignatureHelpProvider { ) signature.label += `): ${member.ReturnType ? member.ReturnType.Name : "unknown"}` - signature.documentation = createDocumentationString(member, "function", klass.Name) + signature.documentation = createDocumentationString(member, "Function", klass.Name) functionClassInformations[member.Name].push(signature) } @@ -95,7 +95,7 @@ export class RobloxSignatureProvider implements vscode.SignatureHelpProvider { member.Parameters, ) signature.label += ")" - signature.documentation = createDocumentationString(member, "event", klass.Name) + signature.documentation = createDocumentationString(member, "Event", klass.Name) eventClassInformations[member.Name].push(signature) } @@ -140,7 +140,7 @@ export class RobloxSignatureProvider implements vscode.SignatureHelpProvider { context: vscode.SignatureHelpContext, ) { const codeAtLine = document.lineAt(position.line).text.substr(0, position.character) - const codeString = codeAtLine.match(/([\w.:]+)\(([\w"',\s.:()-]*)(\))?$/) + const codeString = codeAtLine.match(/([\w.:]+)\(([\w"',\s.:(-]*)(\))?$/) if (codeString !== null) { const indexString = codeString[1] @@ -150,7 +150,7 @@ export class RobloxSignatureProvider implements vscode.SignatureHelpProvider { return null } - const types = await inferType(document, indexString.trim()) + const types = await inferType(document, position, indexString.trim()) let memberType = types[types.length - 1] let classType = types[types.length - 2] @@ -166,7 +166,6 @@ export class RobloxSignatureProvider implements vscode.SignatureHelpProvider { return } - console.log(memberType, classType, isEvent) const [ functionHelpers, eventHelpers ] = classType.Category === "Class" ? await this.classHelpers : await this.structHelpers