Service & Module Autocomplete

I understand it but I find it pointless as it doesn’t really add to or improve on anything that’s in a meaningful manor (imo).

Sure it could save time by typing it all out but if you type fast then its pointless really other than that. I believe I’ve seen other plugins that do the exact same thing in the past too if I’m correct.

Sorry, but i don’t understand how its “pointless”


There is no way you could type faster than using the plugin, typing fast as i could took me 10~ seconds, with plugin it was half a second, 20 times faster.

4 Likes

That’s fine I’m just stating my opinion.

1 Like

If you want to make a GetService autocomplete that’s actually useful, consider automatically inserting services on autocomplete if they don’t already exist in the script.

This is a workflow I have in VS Code with the Roblox LSP plugin and it saves a few seconds each time.

1 Like

elaborate please, i don’t get your question?

Sorry if it was unclear. Here’s a GIF of the auto-import feature in action!

Code_k5C9WK7qRU

tbh, a quick Google search found already two resources who did this for the script editor in Roblox Studio:

1 Like

I actually tried it, and it didn’t work. So i created this post. There won’t be anymore updates, thanks tho.

Dang your resource was the only one that worked for me as well, it’s a shame that you will give up on it :frowning: . I might try reworking your code in the future if you want

Haha! Go for it, I’ve actually made it so you can just type in the service name as you are going but it’s buggy. Do know, it is very much possible and if you do lmk. I will update the resource.

Alright great! I made a github repo for the project in case you or someone else would like to help out

-- Handler.lua
--!nocheck
local ScriptEditorService = game:GetService("ScriptEditorService")
local ServerScriptService = game:GetService("ServerScriptService")

local AutocompleteHelper = require(script:WaitForChild("HelperFunctions"))
-- local WidgetHandler = require(script:WaitForChild("WidgetHandler"))
local Modules = {}
local Services = {}
local Active = false

--> Types
type Request = {
	position: {
		line: number,
		character: number,
	},
	textDocument: {
		document: ScriptDocument?,
		script: LuaSourceContainer?,
	},
}

type Response = {
	items: {
		{
			label: string,
			kind: Enum.CompletionItemKind?,
			tags: { Enum.CompletionItemTag }?,
			detail: string?,
			documentation: {
				value: string,
			}?,
			overloads: number?,
			learnMoreLink: string?,
			codeSample: string?,
			preselect: boolean?,
			textEdit: {
				newText: string,
				replace: {
					start: { line: number, character: number },
					["end"]: { line: number, character: number },
				},
			}?,
		}
	},
}

--> Functions
local function onChange(document: ScriptDocument, changesArray)
	local lineValues = document:GetLine()
	local Symbol = script.Parent.Symbol

	-- original hacky code xD
	Active = string.match(lineValues, Symbol.Value) ~= nil
end

local function onAdded(descendant: Instance)
	if not AutocompleteHelper.isModuleScript(descendant) then return end

	table.insert(Modules, descendant)
end

local function onRemoving(descendant: Instance)
	if not AutocompleteHelper.isModuleScript(descendant) then return end

	for i, module in ipairs(Modules) do
		if module == descendant then
			table.remove(Modules, i)
			break
		end
	end
end


local function getAllModulesAndServices()
	Services = {}
	Modules = {}
	
	for _, instance in ipairs(game:GetChildren()) do
		if AutocompleteHelper.isService(instance) then
			table.insert(Services, instance)
		end
	end

	for _, module in ipairs(game:GetDescendants()) do
		if AutocompleteHelper.isModuleScript(module) then
			table.insert(Modules, module)
		end
	end
end

local function autocompleteCallback(request, response)

	if not Active then 
		return response 
	end

	local replaceTemplate = nil
	
	for _, item in ipairs(response.items) do
		if item.textEdit then
			replaceTemplate = table.clone(item.textEdit.replace)
			replaceTemplate.start.character -= 1
			break
		end
	end

	if not replaceTemplate then return response end

	-- modules
	for _, module in ipairs(Modules) do
		local moduleName = tostring(module)

		if not AutocompleteHelper.shouldProcessName(moduleName) then continue end

		local moduleService = AutocompleteHelper.getServiceForModule(module)

		if not moduleService then continue end

		local path = AutocompleteHelper.getModuleFullNamePath(module)
		local finalText = AutocompleteHelper.getModuleInitializationString(moduleService, moduleName, path, request.textDocument.document)

		local item = {
			label = moduleName,
			detail = path,
			textEdit = {
				newText = finalText,
				replace = replaceTemplate
			}
		}

		table.insert(response.items, item)
	end

	-- services
	for _, service in ipairs(Services) do
		local serviceName = service.ClassName

		if not AutocompleteHelper.shouldProcessName(serviceName) then continue end

		local finalText = AutocompleteHelper.getServiceInitializationString(serviceName)

		local item = {
			label = serviceName,
			detail = "Service",
			textEdit = {
				newText = finalText,
				replace = replaceTemplate
			}
		}

		table.insert(response.items, item)
	end

	return response
end

local function createToolbar()
	-- local toolbar = plugin:CreateToolbar("AutoCompleteModules")
	-- local newButton = toolbar:CreateButton("Symbol Change", "Change the symbol used for autocomplete", "rbxassetid://11963352805")
	-- 
	-- local Widget = WidgetHandler:CreateWidget(plugin) -- module scripts dont have the plugin object so we pass dat
	-- 
	-- newButton.Click:Connect(function()
	-- 	Widget.Enabled = not Widget.Enabled 
	-- end)
	-- 
	-- Widget:BindToClose(function()
	-- 	newButton:SetActive(false)
	-- end)
end

--> Init
pcall(function()
	ScriptEditorService:DeregisterAutocompleteCallback("somenameitreallydoesntmatter")
end)

ScriptEditorService:RegisterAutocompleteCallback("somenameitreallydoesntmatter", 69, autocompleteCallback)
ScriptEditorService.TextDocumentDidChange:Connect(onChange)

game.DescendantAdded:Connect(onAdded)
game.DescendantRemoving:Connect(onRemoving)

getAllModulesAndServices()
-- HelperFunctions.lua
local AutocompleteHelper = {}

function AutocompleteHelper.isModuleScript(instance: Instance): boolean
	return instance.ClassName == "ModuleScript" and not instance:IsDescendantOf(game.CoreGui)
end

function AutocompleteHelper.isService(instance: Instance): boolean
	local service 
	
	local success, err = pcall(function()
		service = game:FindService(instance.ClassName)
	end)
	
	if success then
		return true
	else 
		return false
	end
end

function AutocompleteHelper.getServiceForModule(module: Instance): string?
	for _, service in pairs(game:GetChildren()) do
		if module:IsDescendantOf(service) then
			return tostring(service)
		end
	end
	return nil
end

function AutocompleteHelper.getModuleFullNamePath(module: Instance): string
	return module:GetFullName()
end

function AutocompleteHelper.shouldProcessName(name: string): boolean
	return name:match("[%w]+") == name and name:match("%d") == nil
end

function AutocompleteHelper.getModuleInitializationString(moduleService: string, moduleName: string, path: string, document: ScriptDocument): string
	local serviceAbstraction = string.format('local %s = game:GetService("%s")', moduleService, moduleService)
	local moduleAbstraction = string.format('local %s = require(%s)', moduleName, path)

	for i = 1, document:GetLineCount() do
		local lineString = document:GetLine(i)
		if lineString:match("local " .. moduleService) then
			return moduleAbstraction
		end
	end

	return serviceAbstraction .. "\n" .. moduleAbstraction
end

function AutocompleteHelper.getServiceInitializationString(serviceName: string): string
	return string.format('local %s = game:GetService("%s")', serviceName, serviceName)
end

return AutocompleteHelper

Heres the original code ^^

Alright i added everything to github. I think a good implementation i could make would be to instead of using prefix use directly the service name

Your opinions are: pointless because I type fast, a few plugins do that, use AI (can’t require modules like the plugin does), customisation is pointless… etc.

Just quit hating, every plugin helps people or not. I have sleitnick’s autocomplete plugin and it is way better for me to begin scripting by requiring all necessary modules, we all noticed this plugin won’t help you because you type as fast as an AI so why don’t you just ignore this topic???

Im not.


im just sharing my opinion that’s all. it was also seemly ended days ago. So…

I don’t care if it was days ago, your opinions are just useless and you’re just flooding the entire topic with yapping. So…

I don’t know why you’re this stressed over me expressing my opinion.

He is right tho. Constructive crictisism would have been better. It’s pointless commenting on others work an opinion that dosen’t help nobody.

1 Like

Alright, well it has been more than over for a bit now so id rather not keep going on about this.

Fair enough, but please for the next time try to give a more appropiate feedback on others developer open source creation such as telling them any bugs or how they could improve .

1 Like

You can actually do this by setting the symbol to nothing