Add Godot Provider launcher plugin. (#141)
* Add Godot Provider launcher plugin. * Add 'nohup' as a dependency in plugin.toml
This commit is contained in:
@@ -0,0 +1,43 @@
|
|||||||
|
# Godot Provider
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Godot Provider integrates all projects within the project manager with the
|
||||||
|
Noctalia launcher so you can open a project quickly without leaving the shell.
|
||||||
|
|
||||||
|
## Plugin
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
| --- | --- |
|
||||||
|
| ID | `ramosdetrigo/godot-provider` |
|
||||||
|
| Entry | Launcher provider: `provider` |
|
||||||
|
| Launcher Prefix | `/gd` |
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
Install [Godot](https://godotengine.org/) and ensure `godot` is available on
|
||||||
|
`PATH` as `godot`. Alternatively, you can set the path to the godot binary in
|
||||||
|
the plugin's settings. The `nohup` command is required to launch Godot.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Open the Noctalia launcher and type `/gd` to list local Godot projects.
|
||||||
|
Continue typing to filter projects by name, then select one to open it with
|
||||||
|
`godot`.
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
|
||||||
|
- `sort_by` — Sort projects by last used or most opened projects in the launcher.
|
||||||
|
- `projects_path` — The path to Godot's projets.cfg file.
|
||||||
|
- `godot_command` — The shell command to open godot.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Projects are read from Godot's projects.cfg file, typically at
|
||||||
|
`~/.local/share/godot/projets.cfg`. The list is cached for the launcher session
|
||||||
|
and refreshed when you clear the query.
|
||||||
|
|
||||||
|
The `sort_by` setting sorts by writing the project's ids in a json inside the plugins'
|
||||||
|
[`pluginDataDir`](https://docs.noctalia.dev/v5/plugins/development/runtime-api/?section=filesystem#filesystem).
|
||||||
|
|
||||||
|
Small parts of the code were taken or based upon the [zed-provider](https://github.com/noctalia-dev/community-plugins/tree/main/zed-provider) plugin.
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
local HOME = noctalia.getenv("HOME") or ""
|
||||||
|
local tinsert = table.insert
|
||||||
|
local tremove = table.remove
|
||||||
|
local tfind = table.find
|
||||||
|
local tsort = table.sort
|
||||||
|
local sformat = string.format
|
||||||
|
|
||||||
|
local projectsCache
|
||||||
|
local projectsPath: string = noctalia.getConfig("projects_path") or noctalia.expandPath("~/.local/share/godot/projects.cfg")
|
||||||
|
local godot_command: string = noctalia.getConfig("godot_command") or "godot"
|
||||||
|
local sort_by: string = noctalia.getConfig("sort_by") or "history"
|
||||||
|
local dataDir = noctalia.pluginDataDir()
|
||||||
|
|
||||||
|
if godot_command == "" then godot_command = "godot" end
|
||||||
|
|
||||||
|
local function shellQuote(s: string): string
|
||||||
|
return "'" .. s:gsub("'", "'\"'\"'") .. "'"
|
||||||
|
end
|
||||||
|
|
||||||
|
local function trim(s: string): string
|
||||||
|
return s:match("^%s*(.-)%s*$") :: string
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Gets the project name in project.godot - fallbacks to the folder name
|
||||||
|
local function projectName(path: string): string
|
||||||
|
local p_godot: string = noctalia.readFile(path .. "/project.godot") or ""
|
||||||
|
local project_name = p_godot:match('config/name="(.-)"%s*\n')
|
||||||
|
|
||||||
|
return project_name and project_name:gsub("\\(.)", "%1") or path:match("([^/]+)$") or path
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get table with projects
|
||||||
|
local function getProjects(file_string: string, query: string)
|
||||||
|
local projects = {}
|
||||||
|
|
||||||
|
-- Gets the projects inside the projects.cfg (They're wrapped in "[]")
|
||||||
|
for project_path in file_string:gmatch("%[([^%]]+)%]") do
|
||||||
|
local project_name: string = projectName(project_path)
|
||||||
|
tinsert(projects, {
|
||||||
|
id = project_path,
|
||||||
|
title = project_name,
|
||||||
|
subtitle = project_path:gsub(HOME, "~"),
|
||||||
|
icon = "godot",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- sort alphabetically
|
||||||
|
tsort(projects, function(a, b) return a.title < b.title end)
|
||||||
|
|
||||||
|
return projects
|
||||||
|
end
|
||||||
|
|
||||||
|
-- filter the projects based on the query with fuzzyScore
|
||||||
|
function filterProjects(query, projects)
|
||||||
|
local result = {}
|
||||||
|
for i = 1, #projects do
|
||||||
|
local project = projects[i]
|
||||||
|
|
||||||
|
-- score from title or id
|
||||||
|
local project_score: number?
|
||||||
|
|
||||||
|
if query == "" then
|
||||||
|
-- query empty: sort based on the sort_by key
|
||||||
|
project_score = getSortScore(project.id)
|
||||||
|
else
|
||||||
|
project_score = noctalia.fuzzyScore(query, project.title) or noctalia.fuzzyScore(query, project.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
if query == "" or project_score ~= nil then
|
||||||
|
project.score = project_score -- this will be overriden every time so no problem
|
||||||
|
tinsert(result, project)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- sends launcher results, warning the user if no projects found
|
||||||
|
function showResults(query, projects)
|
||||||
|
if #projects > 0 then
|
||||||
|
launcher.setResults(query, projects)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
launcher.setResults(query, {
|
||||||
|
{
|
||||||
|
id = "",
|
||||||
|
title = noctalia.tr("no-projects-found"),
|
||||||
|
subtitle = noctalia.tr("no-projects-subtitle"),
|
||||||
|
glyph = "loader",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function onQuery(query: string)
|
||||||
|
query = trim(query)
|
||||||
|
|
||||||
|
-- resets cache on new prompt
|
||||||
|
if query == "" or projectsCache == nil then
|
||||||
|
local file = noctalia.readFile(projectsPath)
|
||||||
|
projectsCache = if file then getProjects(file, query) else {}
|
||||||
|
end
|
||||||
|
|
||||||
|
launcher.setResults(query, filterProjects(query, projectsCache))
|
||||||
|
end
|
||||||
|
|
||||||
|
function getSortScore(id: string): number
|
||||||
|
if id == "" then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
if dataDir then
|
||||||
|
if sort_by == "history" then
|
||||||
|
local file_path = dataDir .. "/history.json"
|
||||||
|
local file = noctalia.readFile(file_path)
|
||||||
|
local data = {}
|
||||||
|
if file then
|
||||||
|
data = noctalia.json.decode(file)
|
||||||
|
local i = tfind(data, id) or #data + 1
|
||||||
|
local score = #data - i
|
||||||
|
return score
|
||||||
|
end
|
||||||
|
else -- usage_count
|
||||||
|
local file_path = dataDir .. "/usage.json"
|
||||||
|
local file = noctalia.readFile(file_path)
|
||||||
|
if file then
|
||||||
|
return noctalia.json.decode(file)[id] or 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Open godot editor on the selected project
|
||||||
|
function onActivate(id: string)
|
||||||
|
if id == "" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if dataDir then
|
||||||
|
local file: string?
|
||||||
|
local file_path: string = dataDir .. (sort_by == "history" and "/history.json" or "/usage.json")
|
||||||
|
file = noctalia.readFile(file_path)
|
||||||
|
|
||||||
|
if sort_by == "history" then
|
||||||
|
local data = {}
|
||||||
|
if file then data = noctalia.json.decode(file) end
|
||||||
|
|
||||||
|
local i: number? = tfind(data, id)
|
||||||
|
if i then
|
||||||
|
tremove(data, i)
|
||||||
|
tinsert(data, 1, id)
|
||||||
|
else
|
||||||
|
tinsert(data, 1, id)
|
||||||
|
end
|
||||||
|
|
||||||
|
local json_str: string = noctalia.json.encode(data) :: string
|
||||||
|
noctalia.writeFile(file_path, json_str)
|
||||||
|
else -- usage_count
|
||||||
|
local data = {}
|
||||||
|
if file then data = noctalia.json.decode(file) end
|
||||||
|
data[id] = data[id] and data[id] + 1 or 1
|
||||||
|
local json_str = noctalia.json.encode(data)
|
||||||
|
noctalia.writeFile(file_path, json_str)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
noctalia.runAsync(sformat("nohup %s -e %s &>/dev/null &", shellQuote(godot_command), shellQuote(id.."/project.godot")))
|
||||||
|
end
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
id = "ramosdetrigo/godot-provider"
|
||||||
|
name = "Godot Provider"
|
||||||
|
author = "ramosdetrigo"
|
||||||
|
version = "1.0.0"
|
||||||
|
plugin_api = 3
|
||||||
|
license = "MIT"
|
||||||
|
dependencies = ["godot", "nohup"]
|
||||||
|
icon = "folder-open"
|
||||||
|
description = "Open a recent Godot project from the launcher. Type /gd to list your projects."
|
||||||
|
tags = ["launcher", "development", "productivity"]
|
||||||
|
|
||||||
|
[[setting]]
|
||||||
|
key = "sort_by"
|
||||||
|
type = "select"
|
||||||
|
label_key = "settings.sort_by.label"
|
||||||
|
description_key = "settings.sort_by.description"
|
||||||
|
default = "history"
|
||||||
|
options = [
|
||||||
|
{label_key="settings.history", value = "history"},
|
||||||
|
{label_key="settings.usage_count", value = "usage_count"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[setting]]
|
||||||
|
key = "projects_path"
|
||||||
|
type = "file"
|
||||||
|
label_key = "settings.projects_path.label"
|
||||||
|
description_key = "settings.projects_path.description"
|
||||||
|
default = "~/.local/share/godot/projects.cfg"
|
||||||
|
|
||||||
|
[[setting]]
|
||||||
|
key = "godot_command"
|
||||||
|
type = "string"
|
||||||
|
label_key = "settings.godot_command.label"
|
||||||
|
description_key = "settings.godot_command.description"
|
||||||
|
default = "godot"
|
||||||
|
|
||||||
|
[[launcher_provider]]
|
||||||
|
id = "provider"
|
||||||
|
entry = "godot_provider.luau"
|
||||||
|
prefix = "gd"
|
||||||
|
glyph = "robot"
|
||||||
|
include_in_global_search = false
|
||||||
|
debounce_ms = 0
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"no-projects-found": "No projects found",
|
||||||
|
"no-projects-subtitle": "No godot projects.cfg or empty file",
|
||||||
|
"settings": {
|
||||||
|
"history": "History",
|
||||||
|
"usage_count": "Usage count",
|
||||||
|
"projects_path": {
|
||||||
|
"description": "Path to Godot's projets.cfg file.",
|
||||||
|
"label": "Godot projets.cfg path"
|
||||||
|
},
|
||||||
|
"sort_by": {
|
||||||
|
"description": "Sort by last used or most opened projects.",
|
||||||
|
"label": "Sort by"
|
||||||
|
},
|
||||||
|
"godot_command": {
|
||||||
|
"description": "The shell command to open godot.",
|
||||||
|
"label": "Godot command"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"no-projects-found": "Nenhum projeto foi encontrado.",
|
||||||
|
"no-projects-subtitle": "O arquivo projects.cfg não existe ou está vazio.",
|
||||||
|
"settings": {
|
||||||
|
"history": "Histórico",
|
||||||
|
"usage_count": "Número de usos",
|
||||||
|
"projects_path": {
|
||||||
|
"description": "Caminho pro arquivo projets.cfg da Godot.",
|
||||||
|
"label": "Caminho do projets.cfg"
|
||||||
|
},
|
||||||
|
"sort_by": {
|
||||||
|
"description": "Ordenar por histórico de uso ou número de usos.",
|
||||||
|
"label": "Ordenar por"
|
||||||
|
},
|
||||||
|
"godot_command": {
|
||||||
|
"description": "O comando shell pra abrir a Godot.",
|
||||||
|
"label": "Comando godot"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user