Now you know how to create simple Marta plugins. This time let’s touch Marta APIs a bit more. In this tutorial we will create an action that shows a popup with some information about the selected files.
🚩 Create a new single-file plugin, declare a plugin and an action.
Contexts
Marta holds the application state in several kinds of contexts:
- A
GlobalContext
(marta.globalContext
) provides access to global services (application
,actions
). It is created once during the application start-up and exposed asglobalContext
global. WindowContext
is owned by a Marta window. It holds window-local services (panes
) and allows to run actions.PaneContext
is created for each individual pane (tab). It provides access to the pane’s list model.PaneContext
holds a reference to the containingWindowContext
.
As we want to get the selected files, we surely need the instance of a PaneContext
. How we can access it from our action?
In fact, there is one more context kind - an ActionContext
, passed to Action.apply()
.
marta.action {
...
apply = function(context)
...
end
}
ActionContext
holds references to PaneContext
instances for both active and inactive panes. Let’s use it:
apply = function(context)
-- PaneContext for the active pane
local activePane = context.activePane
-- Active pane list model
local model = activePane.model
-- Iterate through all active files
-- Active files are selected files, if at least one file is selected,
-- or a file under cursor. For an empty directory, `#activeFiles == 0`.
for _, file in ipairs(model.activeFiles) do
martax.alert(file.name)
end
end
Create a new single-file plugin and define an action. Implement apply()
as in the listing above.
Check if everything works as expected. An individal popup should be shown for each selected file.
Collecting Info
Let’s join the output data so only a single popup alert will be displayed. We will also add some more information about the items: for a directory, we will enclose the name into square brackets, for ordinary files, we will print the file size. So the final message will look like this:
[Documents]
[Library]
my-document.txt (4 KB)
script.sh (720 bytes)
martax
module already provides a helper function for rendering size so we don’t need to write it by ourselves:
-- Prints "1 KB"
martax.formatSize(1024)
Instead of showing popups for each file, we collect results to the text
variable:
apply = function(context)
local files = context.activePane.model.activeFileInfos
local text = ""
for _, file in ipairs(files) do
local name = file.name
local entity
if file.isFolder then
entity = "[" .. name .. "]"
else
entity = name .. " (" .. martax.formatSize(file.size) .. ")"
end
text = text .. entity .. "\n"
end
martax.alert("Selected files: \n\n" .. text)
end
Note that we used activeFileInfos
instead of activeFiles
in the code fragment above. activeFiles
returns an array of File
objects which are bare pointers to files. They don’t store any file attributes. On the contrary, activeFileInfos
returns an array of FileInfo
with all the attributes we need. FileInfo
attributes are gathered when the folder is loaded, so they’re cached.
Note that some attributes may be not available on particular file systems. For instance, ZIP file system doesn’t store macOS extended attributes, so isApplication
will always return false
.
Final Version
Now everything looks good – except that we do not check if the selection is empty. Let’s add the check:
local files = context.activePane.model.activeFileInfos
if #files == 0 then
martax.alert("No files selected.")
return
end
We can also ask Marta not to run action at all by passing the isApplicable()
function. It accepts an ActionContext
, and returns a boolean. false
means the action should not be executed.
isApplicable = function(context)
return context.activePane.model.hasActiveFiles
end
Now, if you try to run the action inside an empty folder, no popup will appear. Instead, the "Show file information" can't be run in this context.
message will be shown in a status bar.
isApplicable()
is always called right before running an action, so you can be considerably safe avoiding some checks in apply()
. However, we live in a multi-threaded world, and some other application (or even Marta, as it has an operation queue) might change the reality. So, safer is better.
Here’s the final version of our plugin:
plugin {
id = "marta.example.fileinfo",
name = "File information",
apiVersion = "2.2"
}
action {
id = "show",
name = "Show file information",
isApplicable = function(context)
return context.activePane.model.hasActiveFiles
end,
apply = function(context)
local files = context.activePane.model.activeFileInfos
if #files == 0 then
martax.alert("No files selected.")
return
end
local text = ""
for _, file in ipairs(files) do
local name = file.name
local entity
if file.isFolder then
entity = "[" .. name .. "]"
else
entity = name .. " (" .. martax.formatSize(file.size) .. ")"
end
text = text .. entity .. "\n"
end
martax.alert("Files: \n\n" .. text)
end
}