πΈ Offer common functionalities for Salesforce development
- π₯ Apex/Lwc/Aura: push, retrieve, create
- π» Integrated term
- π File diff: local v.s. org
- π€© Target-org icon
- π Org Metadata browsing
- π€ Quick apex test run
- β¨ Test report and code coverage info
- π¦ Enhanced jump-to-definition (Apex)
- π Salesforce CLI
- π’ Nvim v0.10 or newer (why must > 0.10?)
- π¦ Nvim-treesitter with the Apex parser installed (ensure_installed = { "apex", "soql", "sosl" }), e.g., in my settings
- π (Optional) fzf-lua plugin for executing
SFListMdToRetrieve()
andSFListMdTypeToRetrieve()
(Why not telescope.nvim? Because its UI is slow)
Install using Lazy.nvim by adding the following configuration to your setup:
return {
'xixiaofinland/sf.nvim',
dependencies = {
'nvim-treesitter/nvim-treesitter',
'ibhagwan/fzf-lua', -- no need if you don't use listing metadata feature
},
config = function()
require('sf').setup() -- Important to call setup() to initialize the plugin!
end
}
π¨ Notice:
The hotkeys and user commands are ONLY enabled in a sf project
folder (i.e. has .forceignore
or sfdx-project.json
in the root path).
Run :lua require'sf.util'.get_sf_root()
to verify if the current opened file
resides in sf project folder or not.
Custom configuration can be passed into setup()
Below are the default
settings:
require('sf').setup({
-- Unless you want to customize, no need to copy-paste any of these
-- They are applied automatically
-- This plugin has both hotkeys and user commands supplied
-- This flag enable/disable hotkeys while user commands are always enabled
enable_hotkeys = true,
-- Some hotkeys are on "project level" thus always enabled. Examples: "set default org", "fetch org info".
-- Other hotkeys are enabled when only metadata filetypes are loaded in the current buffer. Example: "push/retrieve current metadata file"
-- This list defines what metadata filetypes have the "other hotkeys" enabled.
-- For example, if you want to push/retrieve css files, it needs to be added into this list.
hotkeys_in_filetypes = {
"apex", "sosl", "soql", "javascript", "html"
},
-- Define what metadata to be listed in `list_md_to_retrieve()` (<leader>ml)
-- Salesforce has numerous metadata types. We narrow down the scope of `list_md_to_retrieve()`.
types_to_retrieve = {
"ApexClass",
"ApexTrigger",
"StaticResource",
"LightningComponentBundle"
},
-- Configuration for the integrated terminal
term_config = {
blend = 10, -- background transparency: 0 is fully opaque; 100 is fully transparent
dimensions = {
height = 0.4, -- proportional of the editor height. 0.4 means 40%.
width = 0.8, -- proportional of the editor width. 0.8 means 80%.
x = 0.5, -- starting position of width. Details in `get_dimension()` in raw_term.lua source code.
y = 0.9, -- starting position of height. Details in `get_dimension()` in raw_term.lua source code.
},
},
-- the sf project metadata folder, update this in case you diverged from the default sf folder structure
default_dir = '/force-app/main/default/',
-- the folder this plugin uses to store intermediate data. It's under the sf project root directory.
plugin_folder_name = '/sf_cache/',
-- after the test running with code coverage completes, display uncovered line sign automatically.
-- you can set it to `false`, then manually run toggle_sign command.
auto_display_code_sign = true,
-- code coverage sign icon colors
code_sign_highlight = {
covered = { fg = "#B7F071" }, -- set `fg = ""` to disable this sign icon
uncovered = { fg = "#F07178" }, -- set `fg = ""` to disable this sign icon
},
})
Upon starting Nvim, Sf.nvim executes SfFetchOrgList
to fetch and save
authenticated org names. Display the target_org in your status line to
facilitate command execution against the target org.
Example configuration using lualine.nvim with target_org(xixiao100
):
sections = {
lualine_c = { 'filename', {
"require'sf'.get_target_org()",
} },
This plugin supplies both default hotkeys and user commands.
Default hotkeys can be disabled in Configuration by setting enable_hotkeys to false
.
Default key | function name | User command | Explain |
---|---|---|---|
<leader>ss |
set_target_org | SFSetTargetOrg | set target_org |
<leader>sf |
fetch_org_list | SFFetchOrgList | fetch/refresh orgs info |
<leader><leader> |
toggle_term | SFToggle | terminal toggle |
<leader>sp |
save_and_push | SFSaveAndPush | push current file |
<leader>sr |
retrieve | SFRetrieve | retrieve current file |
<leader>ta |
run_all_tests_in_this_file | SFRunAllTestsInThisFile | run all Apex tests in current file |
<leader>tt |
run_current_test | SFRunCurrentTest | test this under cursor |
<leader>tr |
repeat_last_tests | SFRunCurrentTest | repeat the last test |
<leader>to |
open_test_select | SFOpenTestSelect | open a buffer to select tests |
<leader>ct |
create_ctags | SFCreateCtags | create ctags file |
<leader>sq |
run_highlighted_soql | N/A | Deault key is only enabled in visual model. Highlight selected text will be run as SOQL in the term |
\s |
toggle_sign | N/A | Show/hide line coverage sign icon |
]v |
uncovered_jump_forward | N/A | jump to next test uncovered hunk |
[v |
`uncovered_jump_backward | N/A | jump to last test uncovered hunk |
All keys are listed in :h sf.nvim
or help.txt file.
Example:
- If you have which-key or a similar plugin installed, pressing
<leader>s
will hint to you what keys are enabled as shown in the screenshot below. Remember that default hotkeys are enabled only inside a sf folder.
Type :SF
in Ex mode will list all user commands:
What if the default keys don't meet your requirements?
You can pass any shell command into run()
method to execute it in the integrated
terminal. For instance, require('sf').run('ls -la')
, then define it as your key: vim.keymap.set('n', '<leader>sk', require('sf').run('ls -la'), { noremap = true, silent = true, desc = 'run ls -la in the terminal' })
.
Checking all features via :h sf.nvim
or help.txt file.
Sometimes you don't know what metadata the target org contains, and you want to list them and fetch specific ones. Steps:
- Retrieve the metadata data by running the user command
SFPullMd
. - Run
SFListMdToRetrieve
(orrequire('sf').list_md_to_retrieve()
) to show the list in a pop-up (requires the fzf-lua plugin) and select one to download to local.
Sometimes you want to fetch all files of a certain metadata type (Apex class, LWC, Aura, etc.). You can list them and fetch all of a specific type. Steps:
- Retrieve the metadata types by running the user command
SFPullMdType
. - Run
SFListMdTypeToRetrieve
(orrequire('sf').list_md_type_to_retrieve()
) to show the list in a pop-up (requires the fzf-lua plugin) and select one to download all metadata of this type to local.
There are two categories of test actions.
β¨ Use-case 1: without code coverage info
You can,
- Run all tests in the current file by
<leader>ta
- Run the test under the cursor by
<leader>tt
- Select tests from the current file by
<leader>to
These commands quickly run and verify the pass/fail result.
π Use-case 2: with code coverage info
Use the same hotkeys but capitalize the last letter:
<leader>tA
<leader>tT
<leader>tO
These test results contains code coverage information.
After running these commands successfully, the test result is saved locally, and the covered/uncovered lines are illustrated as sign-icons next to the line number (screenshot below).
𧩠Screenshot
Test finishes in CrudTest.cls
with UNCOVERED LINES: 9,10,11,13,14
and the line coverage in Crud.cls
is indicated with green/red icon
signs.
π Note.
- The line coverage icon shows automatically if the
auto_display_code_sign
setting is set totrue
(default). - Toggle this feature on/off with the
\s
hotkey (orrequire'sf'.toggle_sign()
).
ποΈ Jump to next uncovered hunk
- Use
]v
and[v
to jump to the next/previous uncovered hunk when the toggle_sign feature is enabled.
The integrated terminal is designed to
- accept input from hotkeys and user commands, such as "retrieve current metadata"
<leader>sr
- be a read-only buffer. It's, by design, not allowed to manually type commands
- be disposable. The output text of the previous command is removed when a new command is invoked
- be auto-prompt, in case the terminal is hidden at the moment the command execution completes. This is handy when you have a long-running command.
You can pass any shell command into run()
method to execute it in the integrated
terminal. For instance, require('sf').run('ls -la')
.
Salesforce's Apex LSP (apex-jorje-lsp.jar) offers a jump-to-definition feature, but it's not perfect. You may encounter cases where it doesn't function correctly in certain codebases. To address this, the LSP jump-to-definition is enhanced by ctags.
If you don't yet know what ctags is, it's wise to google "ctags in vim" to prepare a bit more.
Ctags is ideal in this scenario because:
- It is natively supported by Nvim/Vim, although you need to install
ctags
yourself - The default
<C-]>
key in Nvim will first attempt to jump with LSP and fall back to ctags if LSP fails
There are several versions of ctags, this repo uses universal ctags. So you need to install it to use this feature.
require('sf').create_ctags()
generates the ctags file in the project root.
Using the <C-]>
key for jump-to-definition will automatically use both LSP and ctags in order.
require('sf').create_and_list_ctags()
will update ctags and list the tags symbols in fzf-lua
plugin.
- change diff() to use md_type.json to determine .cls -> ApexClass
Please create an issue to discuss your PR before submitting it. This ensures that the PR will be merged.
The PR is highly recommended to be submitted against the dev
branch.
The help.txt
file is auto-generated from the comments with the ---
suffix
before each function in init.lua. The plugin
uses mini.doc
to automatically generate help.txt
from these ---
suffixed comments. Therefore,
add your doc content in init.lua
without touching help.txt
is sufficient.
Thanks to the following people for contributing to this project:
MIT.