Repository: ldelossa/litee.nvim
Branch: main
Commit: 4efaf373322d
Files: 30
Total size: 125.1 KB
Directory structure:
gitextract_v9v813bt/
├── .github/
│ └── FUNDING.yml
├── .gitignore
├── README.md
├── doc/
│ ├── litee.txt
│ └── tags
└── lua/
└── litee/
└── lib/
├── commands.lua
├── config.lua
├── details/
│ └── init.lua
├── highlights/
│ ├── auto.lua
│ └── init.lua
├── icons/
│ └── init.lua
├── init.lua
├── jumps/
│ └── init.lua
├── lsp/
│ ├── hover.lua
│ ├── init.lua
│ └── wrappers.lua
├── navi/
│ └── init.lua
├── notify/
│ └── init.lua
├── panel/
│ ├── autocmds.lua
│ └── init.lua
├── state/
│ ├── autocmds.lua
│ └── init.lua
├── term/
│ └── init.lua
├── tree/
│ ├── init.lua
│ ├── marshal/
│ │ └── init.lua
│ └── node.lua
└── util/
├── buffer.lua
├── init.lua
├── path.lua
└── window.lua
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: [ldelossa]
================================================
FILE: .gitignore
================================================
notes.md
================================================
FILE: README.md
================================================
```
██╗ ██╗████████╗███████╗███████╗ ███╗ ██╗██╗ ██╗██╗███╗ ███╗
██║ ██║╚══██╔══╝██╔════╝██╔════╝ ████╗ ██║██║ ██║██║████╗ ████║ Lightweight
██║ ██║ ██║ █████╗ █████╗ ██╔██╗ ██║██║ ██║██║██╔████╔██║ Integrated
██║ ██║ ██║ ██╔══╝ ██╔══╝ ██║╚██╗██║╚██╗ ██╔╝██║██║╚██╔╝██║ Text
███████╗██║ ██║ ███████╗███████╗██╗██║ ╚████║ ╚████╔╝ ██║██║ ╚═╝ ██║ Editing
╚══════╝╚═╝ ╚═╝ ╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═══╝ ╚═╝╚═╝ ╚═╝ Environment
====================================================================================
```

# litee.nvim
Litee.nvim (pronounced lite) is a library for building "IDE-lite" experiences in Neovim.
By utilizing the "litee" library plugin authors can achieve a consistent experience
across separate plugins.
There are several official litee plugins which can act as a reference for implementing
additional.
## Calltree
https://github.com/ldelossa/litee-calltree.nvim
Analogous to VSCode's "Call Hierarchy" tool, this plugin exposes an explorable tree
of incoming or outgoing calls for a given symbol.
Unlike other Neovim plugins, the tree can be expanded and collapsed to discover
"callers-of-callers" and "callees-of-callees" until you hit a leaf.
## Symboltree
https://github.com/ldelossa/litee-symboltree.nvim
Analogous to VSCode's "Outline" tool, this plugin exposes a live tree of document
symbols for the current file.
The tree is updated as you move around and change files.
## Filetree
https://github.com/ldelossa/litee-filetree.nvim
Analogous to VSCode's "Explorer", this plugin exposes a full feature file explorer
which supports recursive copies, recursive moves, and proper renaming of a file
(more on this in the appropriate section).
## Bookmarks
https://github.com/ldelossa/litee-bookmarks.nvim
This plugin exposes a way to create Bookmarks, pinnable areas of important
code, and organize them into one or more Notebooks.
Notebooks allow you to open and close sets of Bookmarks depending on what
you're working on that day.
# Usage
litee.nvim is a library which other plugins can important and use.
The library has it's own configuration and setup function which can be
viewed in the `doc.txt`.
An example of configuring the library is below:
```
require('litee.lib').setup({
tree = {
icon_set = "codicons"
},
panel = {
orientation = "left",
panel_size = 30
}
})
```
================================================
FILE: doc/litee.txt
================================================
*litee.nvim* litee.nvim
Author: Louis DeLosSantos <louis.delos@gmail.com>
Homepage: <https://github.com/ldelossa/litee.nvim>
License: MIT license
██╗ ██╗████████╗███████╗███████╗ ███╗ ██╗██╗ ██╗██╗███╗ ███╗
██║ ██║╚══██╔══╝██╔════╝██╔════╝ ████╗ ██║██║ ██║██║████╗ ████║ Lightweight
██║ ██║ ██║ █████╗ █████╗ ██╔██╗ ██║██║ ██║██║██╔████╔██║ Integrated
██║ ██║ ██║ ██╔══╝ ██╔══╝ ██║╚██╗██║╚██╗ ██╔╝██║██║╚██╔╝██║ Text
███████╗██║ ██║ ███████╗███████╗██╗██║ ╚████║ ╚████╔╝ ██║██║ ╚═╝ ██║ Editing
╚══════╝╚═╝ ╚═╝ ╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═══╝ ╚═╝╚═╝ ╚═╝ Environment
====================================================================================
CONTENTS *litee-contents*
1 Intro..............................................|litee-intro|
2 Usage..............................................|litee-usage|
3 Library Modules................................|litee-libraries|
4 lib/details..................................|litee-lib-details|
5 lib/highlights............................|litee-lib-highlights|
6 lib/icons......................................|litee-lib-icons|
7 lib/jumps......................................|litee-lib-jumps|
8 lib/lsp..........................................|litee-lib-lsp|
9 lib/navi........................................|litee-lib-navi|
10 lib/notify......................................|litee-lib-navi|
11 lib/panel......................................|litee-lib-panel|
12 lib/state......................................|litee-lib-state|
13 lib/tree........................................|litee-lib-tree|
14 lib/util........................................|litee-lib-util|
15 lib/term........................................|litee-lib-term|
====================================================================================
INTRODUCTION *litee-intro*
Litee.nvim (pronounced lite) is a library for building "IDE-lite" experience in Neovim.
By utilizing the "litee" library plugin authors can achieve a consistent experience
across separate plugins.
There are several official litee plugins which can act as a reference for implementing
additional.
- Calltree
https://github.com/ldelossa/litee-calltree
Analogous to VSCode's "Call Hierarchy" tool, this feature exposes an explorable tree
of incoming or outgoing calls for a given symbol.
Unlike other Neovim plugins, the tree can be expanded and collapsed to discover
"callers-of-callers" and "callees-of-callees" until you hit a leaf.
- Symboltree
https://github.com/ldelossa/litee-symboltree
Analogous to VSCode's "Outline" tool, this feature exposes a live tree of document
symbols for the current file.
The tree is updated as you move around and change files.
- Filetree
https://github.com/ldelossa/litee-filetree
Analogous to VSCode's "Explorer", this feature exposes a full feature file explorer
which supports recursive copies, recursive moves, and proper renaming of a file
(more on this in the appropriate section).
====================================================================================
Usage *litee-usage*
Litee.nvim exports a single lua module called "lib".
Inside "lib" are several sub-modules which expose a facility for plugin authors.
Each lib will be covered in detail in their respective sections.
The library itself has a config object which configures each sub-module.
The configuration is structured like so (with defaults).
>
M.config = {
icons = {},
jumps = {},
lsp = {},
navi = {},
notify = {
enabled = true,
},
panel = {
orientation = "left",
panel_size = 30,
},
state = {},
tree = {
icon_set = "default",
indent_guides = true
}
}
<
Each key of the configuration corresponds to a library sub-module.
You can apply configuration to litee/lib by way of it's setup method found
in litee/lib/init.lua, for example:
>
require('litee.lib').setup({
tree = {
icon_set = "codicons"
},
panel = {
orientation = "top",
panel_size = 15
}
})
<
The above overrides the default configuration options for the icon_set used in the
tree sub-module and changes the panel sub-module's orientation and size.
Example for custom icons:
>
-- Provide a custom icon_set which will be merged with the default icon_set.
require('litee.lib').setup{
tree = { icon_set_custom = { Struct = "s" } }
}
-- You can even copy, paste the icon_set in `lib/icons/init.lua`, then
-- modify it and pass it to `icon_set_custom`.
local icon_set_custom = { ... }
require('litee.lib').setup{
tree = { icon_set_custom = icon_set_custom }
}
-- Provide a custom icon_set which will be merged with the specified icon_set
-- from `lib/icons/init.lua` by name.
require('litee.lib').setup{
tree = { icon_set_custom = { Struct = "s" }, icon_set = "codicons" }
}
<
====================================================================================
lib/details *litee-lib-details*
The `details` library exports a consistent way to display and close a details
pop up window.
The contents of the `details` pop-up is provided by a `detail_func` passed
into the `details_popup` function.
Thus, the caller can determine what the details are for a particular node or
plugin element.
The `details` object was designed to be used with a tree node, however passing
in any type for the "node" argument will work as long as the associated "detail_func"
can parse the object and spit out a set of buffer lines.
Use `close_details_popup()` to close the pop-up.
====================================================================================
lib/highlights *litee-lib-highlights*
The `highlights` library defines the highlights used within the `litee` library,
methods for setting up the default highlights, and a sub-module for the
auto-highlights features.
The following highlights are exported and can be defined for theme overriding.
>
M.hls = {
SymbolDetailHL = "LTSymbolDetail",
SymbolHL = "LTSymbol",
SymbolJumpHL = "LTSymbolJump",
SymbolJumpRefsHL = "LTSymbolJumpRefs",
IndentGuideHL = "LTIndentGuide",
ExpandedGuideHL = "LTExpandedGuide",
CollapsedGuideHL = "LTCollapsedGuide",
SelectFiletreeHL = "LTSelectFiletree"
}
<
The `auto` module provides a facility for highlighting areas of a source file
given a node and a window.
TODO: The `auto` module should not rely on "lib_util.resolve_absolute_file_path"
and "lib_util.resolve_location", as these methods require us to understand the
types of "node" a head of time.
====================================================================================
lib/icons *litee-lib-icons*
The `icons` library defines and exports icon sets which plugin authors may use
for a consistent experience.
Currently `codicons`, `simple`, `nerd` and `default` icons are exported.
The `default` icon set only supplies the stock UTF-8 icons necessary to display
litee's UI such as indent guides and tree symbols.
Users of this library can override the exported icon libraries during runtime to
change an icon from its default.
This library also exports a list of icon highlights which can be overriden.
Check out the source file `lib/icons/init.lua` for full details.
====================================================================================
lib/jumps *litee-lib-jumps*
The `jumps` library defines and exports functions which perform source file jumps
to a specific "location" object.
The "location" object is specified by the LSP, see:
https://microsoft.github.io/language-server-protocol/specifications/specification-current/#location
Highlights can be set within the jumped-to source window and can be cleared with the `set_jump_hl`
method.
TODO: refactor jumps library to no longer need "lib_util.resolve_location" - all location details
should be passed in.
====================================================================================
lib/lsp *litee-lib-lsp*
The `lsp` library holds methods specific to interacting with the native LSP
library.
This includes helpers for making various LSP requests, wrappers for LSP methods
and a library for creating Hover pop-ups.
This is the library to use or contribute to if you need to interface with
Neovim's native LSP libraries.
====================================================================================
lib/navi *litee-lib-navi*
The `navi` library is small and exports methods for progressing a cursor forward
or backwards in a window which is registered with litee.
Two callbacks exist to perform actions before and after the cursor move.
See source file for details.
See `litee-lib-panel` for information on registration.
====================================================================================
lib/notify *litee-lib-notify*
The `notify` library produces pop-up notifications at the top right corner
of the editor.
Methods exist to create both persistent notifications and ones with configurable
timeouts, along with methods to close all notifications.
Currently, notifications stack on top of each other so only one is visible at a time.
====================================================================================
lib/panel *litee-lib-panel*
The `panel` library provides a consistent panel experience across plugins which
utilizes the `litee.nvim` library.
The panel is akin to panels in JetBrains and VSCode IDEs.
For a plugin to integrate with the panel it must register itself with the panel
and it also must utilize "lib/state" to store its runtime state.
Runtime state holds information about a registered plugins windows, tab, buffer,
etc, and is formally defined in `lib/state`
To register a plugin with the panel the `litee.lib.panel.register_component` method
must be used.
This method requires a `pre_window_create` callback and optionally takes a
`post_window_create` callback.
The `pre_window_create` callback must create a buffer for the plugin being
registered and write the buffer ID into the plugin's component state, which is
an argument to this callback. The plugin is free to perform any other actions
associated with displaying its window, such as setting up autocommands or buffer options.
The `post_window_create` method is optional and when called Neovim is inside the
newly created window within the panel. The plugin is free to perform any in-window
manipulations such as setting syntax highlights or window options.
The panel also supports a feature dubbed "PopOut" panels.
"PopOut" panels are floating windows which act as if you're "popping out" the
panel window to a float, and will "pop" back into the panel when closed.
This is convenient for "zooming" into panel windows.
Additionally, the panel need not be visible for a "PopOut" panel to be displayed.
Meaning, users of `litee.nvim` do not necessary need to interface with the panel,
and can use only "PopOut" panels if they prefer, tho the plugin has to facilitate
this by calling the right `lib/panel` api functions.
For more info on "PopOut" panels see the "popout_to" function in `litee.lib.panel`.
The rest of the API footprint exists to display/hide/jump into the panel.
See the source code for full details.
====================================================================================
lib/state *litee-lib-state*
The `state` library provides a library for handling plugin state.
Litee must keep track of when components (plugins) are displayed in the panel,
if their windows are valid, if they have an associated tree handle, whether their
buffer is valid, etc...
Likewise, plugins need a way to retrieve and store their own state in between
function calls.
The `state` library acts a registry for this state and makes it retrievable both
globally and by components.
A component should only mutate its own state, unless it positively knows what
its doing.
The state structure looks as follows:
>
{
"componentA" = {
buf = 0,
win = 0,
tab = 0,
tree = 0,
win_dimensions = {0, 0},
invoking_win = 0,
active_lsp_clients = {list of lsp clients},
(component specific fields)...
},
"componentB" = {
buf = 0,
win = 0,
tab = 0,
tree = 0,
win_dimensions = {0, 0},
invoking_win = 0,
active_lsp_clients = {list of lsp clients},
(component specific fields)...
}
}
<
When functions expect the full state the function parameter is typically "state" and when
functions expect a component's stat (state["componentA"] for example) the parameter
is typically "component_state".
Ideally all of a plugin's methods which interface with litee require a "component_state",
and they should only touch their own state.
Accessors methods for both global and component state exist in `lib/state` along with methods
to write both types of state back to the registry.
Neovim is single threaded and tables are references, so its actually pretty uncommon to
write state back into the registry. The exception is when setting the initial component
state table in the global state table, performed by `put_component_state`
`lib/state.lua` exports some helpful methods such as getting the component type for
a particular buffer ID. Like always, until I have time to document better, check out the
source for full details.
====================================================================================
lib/tree *litee-lib-tree*
The `tree` library implements a reusable tree which supports the expansion and
collapsing of nodes.
A node in the tree has a core data structure which a plugin can tag on plugin-specific
data on.
A node looks as follows and can be instantiated with the "lib.tree.node.new_node()"
method.
>
{
-- a non-unique display name for the node
name = name,
-- the depth of the node in the target tree
depth = depth,
-- a unique key used to identify the node when
-- placed into a tree
key = key,
-- a list of children nodes with recursive definitions.
children = {},
-- whether this node is expanded in its containing
-- tree.
expanded = false,
}
<
Its the caller's job to define each value ensuring the key for the node is unique within
the tree being built.
The `tree` lib itself is a registry where you request a tree and get back a handle to it.
The handle can be stored on a plugin's state object and stored in `lib/state` and it also
passed to `lib/tree` methods to identify which tree is being acted upon.
The data structure which represents a tree looks like this:
`{root = {}, depth_table = {}, kind = "", source_line_map = nil, buf_line_map = nil}`
Where root is an empty root node, depth_table is a flattened 2d array containing a list of nodes
at a particular depth of the tree (useful for quick lookups), kind is the kind of tree defined
by the caller, source_line_map is a mapping from a source code line to a node in the tree, and
buf_line_map is a mapping from a buffer line in the tree to a source code line.
source_line_map can only be realized if your node has a top level "location" key with an LSP
specified `location` structure as its value.
Both aforementioned maps make it possibe to map items in the tree to source code lines and
vice versa. The best way to understand this is looking at the reference implementations of
"litee-filetree", "litee-calltree", and "litee-symboltree".
Once a tree is created you can begin building its nodes.
A tree is build by creating a root node and then a list of children.
For convenience, the `add_node` function will take a root and a list of children,
compute the children's depth, attach the children to the root, and add it to the tree.
It's important to understand that by using the `add_node` without further options, you
do not need to worry about the children node's depth fields, but you DO need to always
set the root's depth field.
When the `add_node` method receives a root node with its depth field of 0 it throws
away the old tree and creates a new one.
When the `add_node` method receives a root node with a depth greater then zero it can
compute its children's depths and will add this new root (of a sub-tree) to the existing
tree at the appropriate depth.
Sometimes, the caller wants to build the entire tree with their own business logic and
simply use the `tree` library for marshaling into a Neovim buffer and other facilities
such as expand and collapsing. If this is the case you can use the `external` flag
to the 'add_node' method.
When the `add_node` method sees the external flag is set to true, it performs no
actions on the root and simply adds it to the tree structure outlined above. The
`children` parameter is completely ignored.
Once the tree is built and all the desired nodes are added, you can write the tree
to a buffer with the "write_tree" method.
`function M.write_tree(buf, tree, marshal_func, no_guide_leaf)`
The write_tree method takes a buffer ID to write too, a tree ID to write, a marshal_func
which is called for each node and returns the necessary strings which will make up the
buffer line in the tree, and a "no_guide_leaf" flag. See the method's documentation
for marshal_func details.
The `no_guide_leaf` flag tells the `write_tree` method that when a node is expanded,
and it has no children, leave off the "expanded" guide (the down arrow showing its expanded).
This is a nice effect for some trees and a helper method exists if your plugin will always
use this method `function M.write_tree_no_guide_leaf(buf, tree, marshal_func)`.
Make note, the `write_tree` function can only determine a leaf *after* the node is expanded,
for example, a node is collapsed, you expand it, it has no children, no expanded guide is shown.
There are times where the caller can determine an item is a leaf *before* its expanded, for example,
in a file tree, we know a regular file does not need an expand guide.
To accomplish this the `marshal_func` suports returning an override for the expand guide as its
very last argument. If returned, this symbol is used instead of the expand guide `write_tree`
would use on its own accord. `litee-filetree` uses this to simply return a " " when it sees
a regular file.
There is a method, `function M.marshal_line(linenr, handle)', which takes a tree buffer's linenr
and a tree handle and returns the corresponding node.
A small note on the depth_table. A depth_table is a flattened 2d array which hold a list of nodes
at a particular depth. This makes getting nodes at a particular depth very easy and (maybe...?)
faster then a tree traversal. A depth_table is available after any initial and all subsequent
`add_node` calls.
Various methods exist in the library for manipulating the tree such as collapsing nodes and
removing subtrees. Take a look at the source code to see what is available.
====================================================================================
lib/util *litee-lib-util*
Lib `util` is a dumping ground for various helper functions and utilities.
Lib `util` exports several helpful sub-libraries.
`lib.util.window` For helper functions around Neovim windows
`lib.util.buffer` For helper functions around Neovim buffers
`lib.util.path` For helper functions dealing with file system paths (only linux right now.)
====================================================================================
lib/term *litee-lib-term*
Lib `term` exports a method for opening a Neovim native terminal that is aware
of `litee.nvim` environment.
This terminal can be opened on the top or bottom and is controlled by the "term"
configuration block in the `lib.config` module.
>
term = {
-- can be "top" or "bottom"
position = "bottom",
-- the initial size of the terminal
term_size = 15,
-- if true maps arrow keys to window resize
-- commands.
map_resize_keys = true,
},
<
The terminal creates two "terminal" mode key bindings.
>
vim.api.nvim_buf_set_keymap(buf, 't', "<C-w>v", "<cmd>lua require('configs.terminal').terminal_vsplit()<cr>", opts)
vim.api.nvim_buf_set_keymap(buf, 't', "<C-w>n", "<C-\\><C-n>", opts)
vim.api.nvim_buf_set_keymap(buf, 't', "<C-w>h", "<C-\\><C-n> <C-w>h", opts)
vim.api.nvim_buf_set_keymap(buf, 't', "<C-w>j", "<C-\\><C-n> <C-w>j", opts)
vim.api.nvim_buf_set_keymap(buf, 't', "<C-w>k", "<C-\\><C-n> <C-w>k", opts)
vim.api.nvim_buf_set_keymap(buf, 't', "<C-w>l", "<C-\\><C-n> <C-w>l", opts)
<
The first one will open another terminal in a vsplit relative to the terminal you're issuing
the command in.
The second is a helper that puts your terminal back into normal mode.
The final set are short-cuts for jumping out of the terminal into other windows.
Be aware, these mappins will only work when your Vim mode is "terminal" which is when
input is being forwarded directly to the shell. If you are in normal mode (the shell is just
a buffer of lines at that point) the mappins will revert back to normal mode mappings.
The term can be triggered open with the "LTTerm" user command.
vim:ft=help
================================================
FILE: doc/tags
================================================
after litee.txt /*after*
before litee.txt /*before*
litee-contents litee.txt /*litee-contents*
litee-intro litee.txt /*litee-intro*
litee-lib-details litee.txt /*litee-lib-details*
litee-lib-highlights litee.txt /*litee-lib-highlights*
litee-lib-icons litee.txt /*litee-lib-icons*
litee-lib-jumps litee.txt /*litee-lib-jumps*
litee-lib-lsp litee.txt /*litee-lib-lsp*
litee-lib-navi litee.txt /*litee-lib-navi*
litee-lib-notify litee.txt /*litee-lib-notify*
litee-lib-panel litee.txt /*litee-lib-panel*
litee-lib-state litee.txt /*litee-lib-state*
litee-lib-term litee.txt /*litee-lib-term*
litee-lib-tree litee.txt /*litee-lib-tree*
litee-lib-util litee.txt /*litee-lib-util*
litee-usage litee.txt /*litee-usage*
litee.nvim litee.txt /*litee.nvim*
================================================
FILE: lua/litee/lib/commands.lua
================================================
local M = {}
-- commands will setup any Vim commands exported on litee.lib
-- setup.
function M.setup()
vim.cmd("command! LTPanel lua require('litee.lib.panel').toggle_panel()")
vim.cmd("command! LTTerm lua require('litee.lib.term').terminal()")
vim.cmd("command! LTListTerms lua require('litee.lib.term').list_terminals()")
vim.cmd("command! LTClearJumpHL lua require('litee.lib.jumps').set_jump_hl(false)")
vim.cmd("command! LTClosePanelPopOut lua require('litee.lib.panel').close_current_popout()")
end
return M
================================================
FILE: lua/litee/lib/config.lua
================================================
local M = {}
-- config is a global configuration object
-- for each module in the litee library.
--
-- config for a particular module is keyed
-- by the module's directory name.
M.config = {
icons = {},
jumps = {},
lsp = {},
navi = {},
notify = {
enabled = true,
},
panel = {
orientation = "left",
panel_size = 30,
},
state = {},
term = {
position = "bottom",
term_size = 15,
map_resize_keys = true,
},
tree = {
icon_set = "default",
icon_set_custom = nil,
indent_guides = true
}
}
return M
================================================
FILE: lua/litee/lib/details/init.lua
================================================
local M = {}
-- a singleton float window for a details popup.
local float_win = nil
-- close_details_popups closes the created popup window
-- if it exists.
function M.close_details_popup()
if float_win ~= nil and
vim.api.nvim_win_is_valid(float_win) then
vim.api.nvim_win_close(float_win, true)
float_win = nil
end
end
-- details_popup creates a popup window showing futher details
-- about a symbol.
--
-- @param state (table) The global state as defined by
-- the lib/state library.
-- @param node (table) A node passed to `detail_func` representing
-- the item being described.
-- @param detail_func function(state, node) A function called with the
-- provided state and node that returns a list of buffer lines.
-- The function must evaluate both arguments and return a list of buffer
-- lines which describe any details about the node the caller defines.
function M.details_popup(state, node, detail_func)
local buf = vim.api.nvim_create_buf(false, true)
if buf == 0 then
vim.api.nvim_err_writeln("details_popup: could not create details buffer")
return
end
vim.api.nvim_buf_set_option(buf, 'bufhidden', 'delete')
vim.api.nvim_buf_set_option(buf, 'syntax', 'yaml')
local lines = detail_func(state, node)
if lines == nil then
return
end
local width = 20
for _, line in ipairs(lines) do
local line_width = vim.fn.strdisplaywidth(line)
if line_width > width then
width = line_width
end
end
vim.api.nvim_buf_set_option(buf, 'modifiable', true)
vim.api.nvim_buf_set_lines(buf, 0, #lines, false, lines)
vim.api.nvim_buf_set_option(buf, 'modifiable', false)
local popup_conf = vim.lsp.util.make_floating_popup_options(
width,
#lines,
{
border= "rounded",
focusable= false,
zindex = 99,
}
)
float_win = vim.api.nvim_open_win(buf, false, popup_conf)
end
return M
================================================
FILE: lua/litee/lib/highlights/auto.lua
================================================
local lib_util = require('litee.lib.util')
local lib_hi = require('litee.lib.highlights')
local M = {}
-- the current highlight used for auto-highlighting
M.higlight_ns = vim.api.nvim_create_namespace("calltree-hl")
-- highlight will set a highlight on the source code
-- lines the provided node represents if the invoking_win
-- holds a buffer to said file.
--
-- @param node (table) The element representing a source
-- code symbol or element. This function requires the node
-- to have a top level ".location" field.
-- @param set (bool) If false any highlights which were
-- previously set are cleared. If true, highlights will
-- be created.
-- @param win (int) A window handle to the window
-- being evaluated for highlighting.
function M.highlight(node, set, win)
if not vim.api.nvim_win_is_valid(win) then
return
end
local buf = vim.api.nvim_win_get_buf(win)
if not vim.api.nvim_buf_is_valid(buf) then
return
end
vim.api.nvim_buf_clear_namespace(
buf,
M.higlight_ns,
0,
-1
)
if not set then
return
end
local location = node.location
if location == nil then
return
end
local range = location.range
if range["end"] == nil then
return
end
-- make sure URIs match before setting highlight
local invoking_buf = vim.api.nvim_win_get_buf(win)
local cur_file = vim.api.nvim_buf_get_name(invoking_buf)
local symbol_path = lib_util.absolute_path_from_uri(location.uri)
if cur_file ~= symbol_path then
return
end
vim.api.nvim_buf_add_highlight(
buf,
M.higlight_ns,
lib_hi.hls.SymbolJumpHL,
range["start"].line,
range["start"].character,
range["end"].character
)
vim.api.nvim_win_set_cursor(win, {range["start"].line+1, 0})
end
return M
================================================
FILE: lua/litee/lib/highlights/init.lua
================================================
local M = {}
-- hls is a map of UI specific highlights used
-- by the litee.nvim library.
M.hls = {
SymbolDetailHL = "LTSymbolDetail",
SymbolHL = "LTSymbol",
SymbolJumpHL = "LTSymbolJump",
SymbolJumpRefsHL = "LTSymbolJumpRefs",
IndentGuideHL = "LTIndentGuide",
ExpandedGuideHL = "LTExpandedGuide",
CollapsedGuideHL = "LTCollapsedGuide",
SelectFiletreeHL = "LTSelectFiletree",
NormalSB = "LTNormalSB"
}
-- setup_default_highlights configures a list of default
-- highlights for the litee.nvim library.
function M.setup_default_highlights()
local dark = {
LTBoolean = 'hi LTBoolean guifg=#0087af guibg=None',
LTConstant = 'hi LTConstant guifg=#0087af guibg=None',
LTConstructor = 'hi LTConstructor guifg=#4DC5C6 guibg=None',
LTField = 'hi LTField guifg=#0087af guibg=None',
LTFunction = 'hi LTFunction guifg=#988ACF guibg=None',
LTMethod = 'hi LTMethod guifg=#0087af guibg=None',
LTNamespace = 'hi LTNamespace guifg=#87af87 guibg=None',
LTNumber = 'hi LTNumber guifg=#9b885c guibg=None',
LTOperator = 'hi LTOperator guifg=#988ACF guibg=None',
LTParameter = 'hi LTParameter guifg=#988ACF guibg=None',
LTParameterReference = 'hi LTParameterReference guifg=#4DC5C6 guibg=None',
LTString = 'hi LTString guifg=#af5f5f guibg=None',
LTSymbol = 'hi LTSymbol guifg=#87afd7 ',
LTSymbolDetail = 'hi LTSymbolDetail ctermfg=024 cterm=italic guifg=#988ACF gui=italic',
LTSymbolJump = 'hi LTSymbolJump ctermfg=015 ctermbg=110 cterm=italic,bold,underline guifg=#464646 guibg=#87afd7 gui=italic,bold',
LTSymbolJumpRefs = 'hi LTSymbolJumpRefs ctermfg=015 ctermbg=110 cterm=italic,bold,underline guifg=#464646 guibg=#9b885c gui=italic,bold',
LTType = 'hi LTType guifg=#9b885c guibg=None',
LTURI = 'hi LTURI guifg=#988ACF guibg=None',
LTIndentGuide = 'hi LTIndentGuide guifg=None guibg=None',
LTExpandedGuide = 'hi LTExpandedGuide guifg=None guibg=None',
LTCollapsedGuide = 'hi LTCollapsedGuide guifg=None guibg=None',
LTSelectFiletree = 'hi LTSelectFiletree ctermbg=131 ctermfg=246 cterm=None guibg=#af5f5f guifg=#e4e4e4 gui=None'
}
local light = {
LTBoolean = 'hi LTBoolean guifg=#005f87 guibg=None',
LTConstant = 'hi LTConstant guifg=#005f87 guibg=None',
LTConstructor = 'hi LTConstructor guifg=#9b885c guibg=None',
LTField = 'hi LTField guifg=#005f87 guibg=None',
LTFunction = 'hi LTFunction guifg=#806CCF guibg=None',
LTMethod = 'hi LTMethod guifg=#005f87 guibg=None',
LTNamespace = 'hi LTNamespace guifg=#87af87 guibg=None',
LTNumber = 'hi LTNumber guifg=#9b885c guibg=None',
LTOperator = 'hi LTOperator guifg=#806CCF guibg=None',
LTParameter = 'hi LTParameter guifg=#806CCF guibg=None',
LTParameterReference = 'hi LTParameterReference guifg=#268889 guibg=None',
LTString = 'hi LTString guifg=#af5f5f guibg=None',
LTSymbol = 'hi LTSymbol guifg=#806CCF gui=underline',
LTSymbolDetail = 'hi LTSymbolDetail ctermfg=024 cterm=italic guifg=#005f87 gui=italic',
LTSymbolJump = 'hi LTSymbolJump ctermfg=015 ctermbg=110 cterm=italic,bold,underline guifg=#464646 guibg=#87afd7 gui=italic,bold',
LTSymbolJumpRefs = 'hi LTSymbolJumpRefs ctermfg=015 ctermbg=110 cterm=italic,bold,underline guifg=#464646 guibg=#9b885c gui=italic,bold',
LTType = 'hi LTType guifg=#268889 guibg=None',
LTURI = 'hi LTURI guifg=#806CCF guibg=None',
LTIndentGuide = 'hi LTIndentGuide guifg=None guibg=None',
LTExpandedGuide = 'hi LTExpandedGuide guifg=None guibg=None',
LTCollapsedGuide = 'hi LTCollapsedGuide guifg=None guibg=None',
LTSelectFiletree = 'hi LTSelectFiletree ctermbg=131 ctermfg=246 cterm=None guibg=#af5f5f guifg=#e4e4e4 gui=None'
}
local bg = vim.api.nvim_get_option("background")
if bg == "dark" then
for hl_name, hl in pairs(dark) do
if vim.fn.hlexists(hl_name) == 0 then
vim.cmd(hl)
end
end
end
if bg == "light" then
for hl_name, hl in pairs(light) do
if vim.fn.hlexists(hl_name) == 0 then
vim.cmd(hl)
end
end
end
end
return M
================================================
FILE: lua/litee/lib/icons/init.lua
================================================
local M = {}
-- a lot of these are yoinked from:
-- https://github.com/onsails/lspkind-nvim/blob/master/lua/lspkind/init.lua
M.nerd = {
Account = '',
Array = "",
Bookmark = "",
Boolean = "",
Calendar = '',
Check = '',
CheckAll = '',
Circle = '',
CircleFilled = '',
CirclePause = '',
CircleSlash = '',
CircleStop = '',
Class = "ﴯ",
Collapsed = "",
Color = "",
Comment = '',
Constant = "",
Constructor = "",
DiffAdded = '',
Enum = "",
EnumMember = "",
Event = "",
Expanded = "",
Field = "ﰠ",
File = "",
Folder = "",
Function = "",
GitBranch = '',
GitCommit = 'ﰖ',
GitCompare = '',
GitIssue = '',
GitMerge = 'שּׁ',
GitPullRequest = '',
GitRepo = '',
IndentGuide = "⎸",
Info = '',
Interface = "",
Key = "",
Keyword = "",
Method = "",
Module = "",
MultiComment = '',
Namespace = "",
Notebook = "ﴬ",
Null = "ﳠ",
Number = "",
Object = "",
Operator = "",
Package = "",
Pass = '',
PassFilled = '',
Pencil = '',
Property = "ﰠ",
Reference = "",
Separator = "•",
Snippet = "",
Space = " ",
String = "",
Struct = "פּ",
Text = "",
TypeParameter = "",
Unit = "塞",
Value = "",
Variable = "",
}
M.codicons = {
Account = '',
Array = "",
Bookmark = "",
Boolean = "",
Calendar = '',
Check = '',
CheckAll = '',
Circle = '',
CircleFilled = '',
CirclePause = '',
CircleSlash = '',
CircleStop = '',
Class = "",
Collapsed = "",
Color = "",
Comment = '',
CommentExclaim = '',
Constant = "",
Constructor = "",
DiffAdded = '',
Enum = "",
EnumMember = "",
Event = "",
Expanded = "",
Field = "",
File = "",
Folder = "",
Function = "",
GitBranch = '',
GitCommit = '',
GitCompare = '',
GitIssue = '',
GitMerge = '',
GitPullRequest = '',
GitRepo = '',
History = '',
IndentGuide = "⎸",
Info = '',
Interface = "",
Key = "",
Keyword = "",
Method = "",
Module = "",
MultiComment = '',
Namespace = "",
Notebook = "",
Notification = '',
Null = "",
Number = "",
Object = "",
Operator = "",
Package = "",
Pass = '',
PassFilled = '',
Pencil = '',
Property = "",
Reference = "",
RequestChanges = '',
Separator = "•",
Snippet = "",
Space = " ",
String = "",
Struct = "",
Sync = '',
Text = "",
TypeParameter = "",
Unit = "",
Value = "",
Variable = "",
}
M.default = {
Account = "🗣",
Array = "\\[\\]",
Bookmark = "🏷",
Boolean = "∧",
Calendar = '🗓',
Check = '✓',
CheckAll = '🗸🗸',
Circle = '🞆',
CircleFilled = '●',
CirclePause = '⦷',
CircleSlash = '⊘',
CircleStop = '⦻',
Class = "c",
Collapsed = "▶",
Color = "🖌",
Comment = '🗩',
CommentExclaim = '🗩',
Constant = "c",
Constructor = "c",
DiffAdded = '+',
Enum = "Ε",
EnumMember = "Ε",
Event = "🗲",
Expanded = "▼",
Field = "𝐟",
File = "🗀",
Folder = "🗁",
Function = "ƒ",
GitBranch = ' ',
GitCommit = '⫰',
GitCompare = '⤄',
GitIssue = '⊙',
GitMerge = '⫰',
GitPullRequest = '⬰',
GitRepo = '🕮',
History = '⟲',
IndentGuide = "⎸",
Info = '🛈',
Interface = "I",
Key = "",
Keyword = "",
Method = "",
Module = "M",
MultiComment = '🗩',
Namespace = "N",
Notebook = "🕮",
Notification = '🕭',
Null = "null",
Number = "#",
Object = "{}",
Operator = "0",
Package = "{}",
Pass = '🗸',
PassFilled = '🗸',
Pencil = '',
Property = "🛠",
Reference = "⛉",
RequestChanges = '⨪',
Separator = "•",
Space = " ",
String = "\"",
Struct = "struct",
Sync = '🗘',
Text = "\"",
TypeParameter = "T",
Unit = "U",
Value = "v",
Variable = "V",
}
M.simple = {
Account = "A",
Array = "\\[\\]",
Bookmark = "BM",
Boolean = "bool",
Calendar = 'cal',
Check = 'x',
CheckAll = 'xx',
Circle = 'o',
CircleFilled = 'O',
CirclePause = 'P',
CircleSlash = 'ø',
CircleStop = 'stop',
Class = "class",
Collapsed = ">",
Color = "color",
Comment = 'c',
CommentExclaim = 'c!',
Constant = "c",
Constructor = "C",
DiffAdded = '+',
Enum = "Ε",
EnumMember = "Ε",
Event = "e",
Expanded = "v",
Field = "f",
File = "F",
Folder = "D",
Function = "fn",
GitBranch = 'br',
GitCommit = 'ci',
GitCompare = 'cmp',
GitIssue = 'I',
GitMerge = 'M',
GitPullRequest = 'PR',
GitRepo = 'R',
History = 'H',
IndentGuide = " ",
Info = 'i',
Interface = "I",
Key = "k",
Keyword = "kw",
Method = "m",
Module = "M",
MultiComment = '#',
Namespace = "NS",
Notebook = "NB",
Notification = 'N',
Null = "null",
Number = "#",
Object = "{}",
Operator = "op",
Package = "{}",
Pass = 'y',
PassFilled = 'yy',
Pencil = 'e',
Property = "P",
Reference = "&",
RequestChanges = '-',
Separator = ".",
Space = " ",
String = "\"",
Struct = "S",
Sync = 's',
Text = "\"",
TypeParameter = "T",
Unit = "U",
Value = "v",
Variable = "V",
}
M.icon_hls = {
Array = "LTConstant",
Boolean = "LTBoolean",
Class = "LTType",
Constant = "LTConstant",
Constructor = "LTFunction",
Enum = "LTType",
EnumMember = "LTField",
Event = "LTType",
Field = "LTField",
File = "LTURI",
Folder = "LTNamespace",
Function = "LTFunction",
Interface = "LTType",
Key = "LTType",
Keyword = "LTConstant",
Method = "LTFunction",
Module = "LTNamespace",
Namespace = "LTNamespace",
Null = "LTType",
Number = "LTNumber",
Object = "LTType",
Operator = "LTOperator",
Package = "LTNamespace",
Property = "LTMethod",
Reference = "LTType",
Snippet = "LTString",
String = "LTString",
Struct = "LTType",
Text = "LTString",
TypeParameter = "LTParameter",
Unit = "LTType",
Value = "LTType",
Variable = "LTConstant",
Info = 'LTInfo',
Pass = 'LTSuccess',
PassFilled = 'LTSuccess',
Account = 'LTAccount',
Check = 'LTSuccess',
CheckAll = 'LTSuccess',
CircleFilled = 'LTDefault',
Circle = 'LTDefault',
CircleSlash = 'LTWarning',
GitCompare = 'LTGitCompare',
GitBranch = 'LTGitBranch',
GitPullRequest = 'LTGitPullRequest',
CircleStop = 'LTFailure',
DiffAdded = 'LTDiffAdded',
CirclePause = 'LTWarning',
GitCommit = 'LTGitCommit',
Comment = 'LTComment',
MultiComment = 'LTMultiComment',
Calendar = 'LTDefault',
Pencil = 'LTWarning',
History = 'LTWarning',
Sync = 'LTWarning',
CommentExclaim = 'LTWarning',
RequestChanges = 'LTFailure',
Notification = 'LTDiffAdded'
}
return M
================================================
FILE: lua/litee/lib/init.lua
================================================
local commands = require('litee.lib.commands')
local config = require('litee.lib.config').config
local lib_hi = require('litee.lib.highlights')
local lib_icons = require('litee.lib.icons')
local M = {}
-- Once set up, `M.icon_set` must be a non-nil table.
M.icon_set = nil
local function icon_set()
local tree = config.tree
local base = lib_icons[tree.icon_set] or lib_icons["default"]
local icon_set = nil
if tree.icon_set_custom ~= nil then
icon_set = tree.icon_set_custom
-- merge custom icon with the default
for key, val in pairs(base) do
icon_set[key] = icon_set[key] or base[key]
end
else
icon_set = base
end
M.icon_set = icon_set
end
-- If a custom icon_set (table) is provided by the user,
-- `M.icon_set` will be updated and returned.
-- A user can use the icons from `lib_icons`
-- via providing a key name instead of a custom icon_set (table).
-- When both arguments are provided, the custom icon_set is merged
-- with the icons given by its name from `lib_icons`.
function M.icon_set_update(custom, icon_key)
local icon_set = nil
if custom ~= nil then
icon_set = icon_key and lib_icons[icon_key] or M.icon_set
for key, val in pairs(custom) do
icon_set[key] = custom[key]
end
else
icon_set = lib_icons[icon_key] or lib_icons["default"]
end
return icon_set
end
local function merge_subconfig(component, user_subconfig)
local subconfig = config[component]
if subconfig == nil then
return
end
for key, val in pairs(user_subconfig) do
subconfig[key] = val
end
end
function M.setup(user_config)
if user_config ~= nil then
for component, user_subconfig in pairs(user_config) do
merge_subconfig(component, user_subconfig)
end
end
-- setup icon_set
icon_set()
-- setup default highlights
lib_hi.setup_default_highlights()
-- setup commands
commands.setup()
-- au to close popup with cursor moves or buffer is closed.
vim.cmd("au CursorMoved,BufWinLeave,WinLeave * lua require('litee.lib.util.buffer').close_all_popups()")
-- on resize cycle the panel to re-adjust window sizes.
vim.cmd("au VimResized * lua require('litee.lib.panel.autocmds').on_resize()")
-- will clean out any tree data for a tab when closed. only necessary
vim.cmd([[au TabClosed * lua require('litee.lib.state.autocmds').on_tab_closed(vim.fn.expand('<afile>'))]])
end
return M
================================================
FILE: lua/litee/lib/jumps/init.lua
================================================
local config = require('litee.lib.config').config
local lib_hi = require('litee.lib.highlights')
local lib_panel = require('litee.lib.panel')
local M = {}
-- the current highlight source, reset on jumps
M.jump_higlight_ns = vim.api.nvim_create_namespace("calltree-jump")
-- the buffer we highlighted last.
M.last_highlighted_buffer = nil
-- move_or_create will a attempt to move from the
-- calltree ui window to the nearest editor window
--
-- if the move fails, assumingly because no other window
-- exists, a new window will be created.
--
-- @param orientation (string) The orientation of the
-- surrouning plugin UI to make jumps intuitive.
-- This is typically the orientation of the litee.nvim
-- panel. Valid arguments are "left, right, top, bottom".
local function move_or_create(orientation)
local cur_win = vim.api.nvim_get_current_win()
if orientation == "left" then
vim.cmd('wincmd l')
elseif orientation == "right" then
vim.cmd('wincmd h')
elseif orientation == "top" then
vim.cmd('wincmd j')
elseif orientation == "bottom" then
vim.cmd('wincmd k')
end
if cur_win == vim.api.nvim_get_current_win() then
if orientation == "left" then
vim.cmd("botright vsplit")
return true
elseif orientation == "right" then
vim.cmd("topleft vsplit")
return true
elseif orientation == "top" then
vim.cmd("topleft split")
return true
elseif orientation == "bottom" then
vim.cmd("topleft split")
return true
end
end
return false
end
-- jump_tab will open a new tab then jump to the symbol
--
-- @param location (table) A location object as defined by
-- the LSP.
-- @param node (table) An element which is being jumped to,
-- if this element has a high level "references" field with
-- more "Location" objects, they will be highlighted as well.
function M.jump_tab(location, node, offset_encoding)
M.set_jump_hl(false, nil)
vim.cmd("tabedit " .. location.uri)
vim.cmd("set nocursorline")
-- if the panel currently has a component "popped-out"
-- close it before jumping.
lib_panel.close_current_popout()
vim.lsp.util.jump_to_location(location, offset_encoding or "utf-8")
M.set_jump_hl(true, node)
end
-- jump_split will open a new split then jump to the symbol
--
-- @param split (string) The type of split, valid arguments
-- are "split" or "vsplit".
-- @param location (table) A location object as defined by
-- the LSP.
-- @param node (table) An element which is being jumped to,
-- if this element has a high level "references" field with
-- more "Location" objects, they will be highlighted as well.
function M.jump_split(split, location, node, offset_encoding)
M.set_jump_hl(false, nil)
if not move_or_create(config["panel"].orientation) then
vim.cmd(split)
end
-- if the panel currently has a component "popped-out"
-- close it before jumping.
lib_panel.close_current_popout()
vim.lsp.util.jump_to_location(location, offset_encoding or "utf-8")
M.set_jump_hl(true, node)
end
-- jump_neighbor will jump to the symbol using the
-- closest left or right window.
--
-- a window will be created if it does not exist.
--
-- @param location (table) A location object as defined by
-- the LSP.
-- @param node (table) An element which is being jumped to,
-- if this element has a high level "references" field with
-- more "Location" objects, they will be highlighted as well.
function M.jump_neighbor(location, node, offset_encoding)
M.set_jump_hl(false, nil)
move_or_create(config["panel"].orientation)
-- if the panel currently has a component "popped-out"
-- close it before jumping.
lib_panel.close_current_popout()
vim.lsp.util.jump_to_location(location, offset_encoding or "utf-8")
M.set_jump_hl(true, node)
-- cleanup any [No Name] buffers if they exist
for _, buf in ipairs(vim.api.nvim_list_bufs()) do
local name = vim.api.nvim_buf_get_name(buf)
if name == "" then
vim.api.nvim_buf_delete(buf, {force=true})
end
end
end
-- jump_invoking will jump to the symbol using the
-- window that initially invoked the calltree.
--
-- a window is created and seen as the new invoking window
-- if the original invoking window has been closed.
--
-- @param location (table) A location object as defined by
-- the LSP.
-- @param win (int) A window handle of the invoking window
-- to jump to.
-- @param node (table) An element which is being jumped to,
-- if this element has a high level "references" field with
-- more "Location" objects, they will be highlighted as well.
function M.jump_invoking(location, win, node, offset_encoding)
M.set_jump_hl(false, nil)
if not vim.api.nvim_win_is_valid(win) then
if config["panel"].orientation == "left" then
vim.cmd("botright vsplit")
elseif config["panel"].orientation == "right" then
vim.cmd("topleft vsplit")
elseif config["panel"].orientation == "top" then
vim.cmd("topleft split")
elseif config["panel"].orientation == "bottom" then
vim.cmd("topleft split")
end
win = vim.api.nvim_get_current_win()
end
vim.api.nvim_set_current_win(win)
-- if the panel currently has a component "popped-out"
-- close it before jumping.
lib_panel.close_current_popout()
vim.lsp.util.jump_to_location(location, offset_encoding or "utf-8")
M.set_jump_hl(true, node)
-- cleanup any [No Name] buffers if they exist
for _, buf in ipairs(vim.api.nvim_list_bufs()) do
local name = vim.api.nvim_buf_get_name(buf)
if name == "" then
vim.api.nvim_buf_delete(buf, {force=true})
end
end
return win
end
-- set_jump_hl will highlight the symbol and
-- any references to the symbol if set == true.
--
-- @param set (bool) If false highlights any previously created
-- jump highlights will be removed.
--
-- @param node (table) An element which is being jumped to,
-- the node must have a high level ".location" field.
-- if this element has a high level ".references" field with
-- an array of "Range" objects (specified by LSP),
-- they will be highlighted as well.
function M.set_jump_hl(set, node)
if not set then
if
M.last_highlighted_buffer ~= nil
and vim.api.nvim_buf_is_valid(M.last_highlighted_buffer)
then
vim.api.nvim_buf_clear_namespace(
M.last_highlighted_buffer,
M.jump_higlight_ns,
0,
-1
)
end
return
end
M.last_highlighted_buffer = vim.api.nvim_get_current_buf()
-- set highlght for function itself
local location = node.location
if location == nil then
return
end
local range = location.range
vim.api.nvim_buf_add_highlight(
M.last_highlighted_buffer,
M.jump_higlight_ns,
lib_hi.hls.SymbolJumpHL,
range["start"].line,
range["start"].character,
range["end"].character
)
-- apply it to all the references
if node.references ~= nil then
for _, ref in ipairs(node.references) do
vim.api.nvim_buf_add_highlight(
M.last_highlighted_buffer,
M.jump_higlight_ns,
lib_hi.hls.SymbolJumpRefsHL,
ref["start"].line,
ref["start"].character,
ref["end"].character
)
end
end
end
return M
================================================
FILE: lua/litee/lib/lsp/hover.lua
================================================
local M = {}
local float_win = nil
-- close_hover_popups closes the created popup window
-- if it exists.
function M.close_hover_popup()
if float_win ~= nil and
vim.api.nvim_win_is_valid(float_win) then
vim.api.nvim_win_close(float_win, true)
float_win = nil
end
end
-- hover_handle shows hover information for a symbol in a calltree
-- ui window.
--
-- modified from neovim runtime/lua/vim/lsp/handlers.lua
-- function conforms to client LSP handler signature.
function M.hover_handler(_, result, ctx, config)
M.close_hover_popup()
-- get lines from result
config = config or {}
config.focus_id = ctx.method
if not (result and result.contents) then
-- return { 'No information available' }
return
end
local lines = vim.lsp.util.convert_input_to_markdown_lines(result.contents)
lines = vim.lsp.util.trim_empty_lines(lines)
if vim.tbl_isempty(lines) then
-- return { 'No information available' }
return
end
-- create buffer for popup
local buf = vim.api.nvim_create_buf(false, false)
if buf == 0 then
vim.api.nvim_err_writeln("details_popup: could not create details buffer")
return
end
vim.api.nvim_buf_set_option(buf, 'bufhidden', 'delete')
vim.api.nvim_buf_set_option(buf, 'syntax', 'markdown')
vim.api.nvim_buf_set_option(buf, 'filetype', 'markdown')
lines = vim.lsp.util.stylize_markdown(buf, lines, {})
local width = 20
for _, line in ipairs(lines) do
local line_width = vim.fn.strdisplaywidth(line)
if line_width > width then
width = line_width
end
end
vim.api.nvim_buf_set_option(buf, 'modifiable', true)
vim.api.nvim_buf_set_lines(buf, 0, #lines, false, lines)
vim.api.nvim_buf_set_option(buf, 'modifiable', false)
local popup_conf = vim.lsp.util.make_floating_popup_options(
width,
#lines,
{
border= "rounded",
focusable= false,
zindex = 99,
}
)
float_win = vim.api.nvim_open_win(buf, false, popup_conf)
return float_win
end
return M
================================================
FILE: lua/litee/lib/lsp/init.lua
================================================
local lib_notify = require('litee.lib.notify')
local lib_tree_node = require('litee.lib.tree.node')
local M = {}
-- multi_client_request makes an LSP request to multiple clients.
--
-- the signature is the same as an individual LSP client request method
-- but takes a list of clients as the first argument.
function M.multi_client_request(clients, method, params, handler, bufnr)
for _, client in ipairs(clients) do
if client.supports_method(method) then
client.request(method, params, handler, bufnr or 0)
return client
end
end
end
-- gather_sybmbols_async_handler is the async handler
-- for gather_symbols_async method and incrementally
-- builds a tree of symbols from a workspace symbols
-- request.
function M.gather_symbols_async_handler(node, co)
return function(err, result, _, _)
if err ~= nil then
coroutine.resume(co, nil)
return
end
if result == nil then
coroutine.resume(co, nil)
return
end
local start_line, uri = "", ""
if node.call_hierarchy_item ~= nil then
start_line = node.call_hierarchy_item.range.start.line
uri = node.call_hierarchy_item.uri
elseif node.document_symbol ~= nil then
start_line = node.document_symbol.range.start.line
uri = node.uri
end
for _, res in ipairs(result) do
if
res.location.uri == uri and
res.location.range.start.line ==
start_line
then
coroutine.resume(co, res)
return
end
end
coroutine.resume(co, nil)
end
end
-- gather_symbols_async will acquire a list of workspace symbols given
-- a tree of call_hierarchy items.
function M.gather_symbols_async(root, children, component_state, callback)
local co = nil
local all_nodes = {}
table.insert(all_nodes, root)
for _, child in ipairs(children) do
table.insert(all_nodes, child)
end
co = coroutine.create(function()
for i, node in ipairs(all_nodes) do
local params = {
query = node.name,
}
lib_notify.notify_popup("gathering symbols [" .. i .. "/" .. #all_nodes .. "]", "warning")
local client = M.multi_client_request(
component_state.active_lsp_clients,
"workspace/symbol",
params,
-- handler will call resume for this co.
M.gather_symbols_async_handler(node, co)
)
if client.offset_encoding ~= nil then
node.offset_encoding = client.offset_encoding
end
node.symbol = coroutine.yield()
lib_notify.close_notify_popup()
end
callback()
end)
coroutine.resume(co)
end
-- symbol_from_node attempts to extract the workspace
-- symbol the node represents.
--
-- @param clients (table) All active lsp clients
-- @param node (table) A node with a "call_hierarhcy_item"
-- field we are acquiring workspace symbols for.
-- @param bufnr (int) An window handle to the buffer
-- containing the node.
-- @returns (table) the SymbolInformation LSP structure, see:
-- https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#symbolInformation
function M.symbol_from_node(clients, node, bufnr)
local params = {
query = node.name,
}
for _, client in ipairs(clients) do
if not client.supports_method("workspace/symbol") then
goto continue
end
-- not all LSPs are optimized, specially ones in early development, set
-- this timeout high.
local out = client.request_sync("workspace/symbol", params, 5000, bufnr)
if out == nil then
goto continue
end
if out.err ~= nil or (out.result == nil or #out.result <= 0) then
goto continue
end
for _, res in ipairs(out.result) do
if
res.uri == node.uri and
res.location.range.start.line ==
node.call_hierarchy_item.range.start.line
then
return res
end
end
::continue::
end
return nil
end
-- conv_symbolinfo_to_docsymbol will convert a SymbolInformation
-- model to a DocumentSymbol mode.
--
-- this is handy when working with documentSymbol requests and
-- users do not want to handle two data models in their code.
--
-- @param symbolinfo (table) A SymbolInformation structure as
-- defined by the LSP specification.
-- @returns (table) A DocumentSymbol structure as defined by
-- the LSP specification.
function M.conv_symbolinfo_to_docsymbol(symbolinfo)
local document_symbol = {}
-- these are mandatory fields per the LSP spec,
-- return nil if they arent there.
if
symbolinfo.name == nil or
symbolinfo.kind == nil or
symbolinfo.location == nil or
symbolinfo.location.range == nil
then
return nil
end
document_symbol.name = symbolinfo.name
document_symbol.kind = symbolinfo.kind
document_symbol.range = symbolinfo.location.range
document_symbol.children = {}
document_symbol.details = ""
document_symbol.tags = {}
document_symbol.deprecated = false
document_symbol.selectionRange = symbolinfo.location.range
return document_symbol
end
return M
================================================
FILE: lua/litee/lib/lsp/wrappers.lua
================================================
local lib_notify = require('litee.lib.notify')
local M = {}
function M.buf_document_symbol()
lib_notify.notify_popup_with_timeout("Creating document outline...", 7500, "info")
vim.lsp.buf.document_symbol()
end
function M.buf_incoming_calls()
lib_notify.notify_popup_with_timeout("Creating incoming calls outline...", 7500, "info")
vim.lsp.buf.incoming_calls()
end
function M.buf_outgoing_calls()
lib_notify.notify_popup_with_timeout("Creating outgoing calls outline...", 7500, "info")
vim.lsp.buf.outgoing_calls()
end
return M
================================================
FILE: lua/litee/lib/navi/init.lua
================================================
local M = {}
-- next moves the cursor in the window of the provided
-- component_state forward
--
-- @param component_state (table) A state table as defined
-- in lib/state.
-- @param pre_cb function() A callback which fires just before
-- the cursor move.
-- @param post_cb function() A callback which fires just after
-- the cursor move.
function M.next(component_state, pre_cb, post_cb)
if component_state.win == nil
or not vim.api.nvim_win_is_valid(component_state.win) then
return
end
local cur_cursor = vim.api.nvim_win_get_cursor(component_state.win)
local lines_nr = vim.api.nvim_buf_line_count(component_state.buf)
if cur_cursor[1] + 1 > lines_nr then
return
end
cur_cursor[1] = cur_cursor[1] + 1
if pre_cb ~= nil then pre_cb() end
vim.api.nvim_win_set_cursor(component_state.win, cur_cursor)
if post_cb ~= nil then pre_cb() end
end
-- next moves the cursor in the window of the provided
-- component_state backwards
--
-- @param component_state (table) A state table as defined
-- in lib/state.
-- @param pre_cb function() A callback which fires just before
-- the cursor move.
-- @param post_cb function() A callback which fires just after
-- the cursor move.
function M.previous(component_state, pre_cb, post_cb)
if component_state.win == nil
or not vim.api.nvim_win_is_valid(component_state.win) then
return
end
local cur_cursor = vim.api.nvim_win_get_cursor(component_state.win)
if cur_cursor[1] - 1 < 1 then
return
end
cur_cursor[1] = cur_cursor[1] - 1
if pre_cb ~= nil then pre_cb() end
vim.api.nvim_win_set_cursor(component_state.win, cur_cursor)
if post_cb ~= nil then pre_cb() end
end
return M
================================================
FILE: lua/litee/lib/notify/init.lua
================================================
local notify_config = require('litee.lib.config').config["notify"]
local M = {}
local float_wins = {}
function M.close_notify_popup()
if not notify_config.enabled then
return
end
if float_wins == nil then
return
end
for _, float_win in ipairs(float_wins) do
if vim.api.nvim_win_is_valid(float_win) then
vim.api.nvim_win_close(float_win, true)
end
end
float_wins = nil
end
function M.notify_popup_with_timeout(text, ms, sev)
if not notify_config.enabled then
return
end
M.notify_popup(text, sev)
local timer = vim.loop.new_timer()
timer:start(ms, 0, vim.schedule_wrap(
M.close_notify_popup
))
end
function M.notify_popup(text, sev)
if not notify_config.enabled then
return
end
if float_wins == nil then
float_wins = {}
end
local buf = vim.api.nvim_create_buf(false, true)
if buf == 0 then
vim.api.nvim_err_writeln("details_popup: could not create details buffer")
return
end
vim.api.nvim_buf_set_option(buf, 'bufhidden', 'delete')
vim.api.nvim_buf_set_option(buf, 'syntax', 'yaml')
local lines = {text}
local width = 20
local line_width = vim.fn.strdisplaywidth(text)
if line_width > width then
width = line_width
end
vim.api.nvim_buf_set_option(buf, 'modifiable', true)
vim.api.nvim_buf_set_lines(buf, 0, #lines, false, lines)
vim.api.nvim_buf_set_option(buf, 'modifiable', false)
local popup_conf = {
relative = "editor",
anchor = "SE",
width = width,
height = 1,
focusable = false,
zindex = 99,
style = "minimal",
border = "rounded",
row = 0,
col = vim.opt.columns:get(),
}
local float_win = vim.api.nvim_open_win(buf, false, popup_conf)
table.insert(float_wins, float_win)
if sev == "error" then
vim.api.nvim_win_set_option(float_win, 'winhl', "Normal:Error")
elseif sev == "warning" then
vim.api.nvim_win_set_option(float_win, 'winhl', "Normal:WarningMsg")
else
vim.api.nvim_win_set_option(float_win, 'winhl', "Normal:NormalFloat")
end
end
return M
================================================
FILE: lua/litee/lib/panel/autocmds.lua
================================================
local lib_state = require('litee.lib.state')
local lib_panel = require('litee.lib.panel')
local M = {}
function M.on_resize()
local cur_win = vim.api.nvim_get_current_win()
local cur_tab = vim.api.nvim_get_current_tabpage()
local state = lib_state.get_state(cur_tab)
if state == nil then
return
end
local is_open = lib_panel.is_panel_open(state)
if is_open then
lib_panel.toggle_panel(state, false, true)
vim.api.nvim_set_current_win(cur_win)
end
end
return M
================================================
FILE: lua/litee/lib/panel/init.lua
================================================
local lib_state = require('litee.lib.state')
local config = require('litee.lib.config').config
local lib_notify = require('litee.lib.notify')
local lib_util = require('litee.lib.util')
local lib_util_win = require('litee.lib.util.window')
local M = {}
-- components maps a unique component map
-- with a set of callbacks (see M.register_component)
--
-- the order in which components exist in this map
-- determines the component's order in the panel.
local components = {}
-- register_component adds a component to
-- the panel.
--
-- the panel's layout is determined by the
-- order in which components are registered.
--
-- registering a component *must* include a "pre_window_create"
-- callback and can optionally provide a "post_window_create"
-- callback.
--
-- @param component (string) Unique name for a component,
-- subsequent calls for a unique component will over write
-- previous
-- @param pre_window_create (function(state)) A callback which must
-- prepare the provided state object, as defined by lib/state.lua
-- with a valid buffer. The callback is free to perform any other
-- tasks such as writing to the buffer if desired. The buffer
-- ID should be stored in the component's "buf" field in state
-- and will be displayed in the appropriate panel's window on
-- toggle. This callback can return false when the window should
-- not be opened in the panel and true to indicate it should.
-- @param post_window_create (function(state)) A callback which can customize
-- the component window further after being displayed in the panel. Useful for
-- setting up component-specific window options and winhl configurations.
-- The state[component].win field will be populated with the window id of the
-- panel window for further configuration.
function M.register_component(component, pre_window_create, post_window_create)
components[component] = {
pre = pre_window_create,
post = post_window_create
}
end
function M.is_panel_open(state)
local component_open = false
for component, _ in pairs(components) do
if
state[component] ~= nil
and state[component].win ~= nil
and vim.api.nvim_win_is_valid(state[component].win)
then
component_open = true
end
end
return component_open
end
-- toggle_panel will toggle the panel open and
-- close in the common case. Arguments can be
-- used to alter how the toggle takes place.
--
-- @param state (table) The full state table as defined
-- in lib/state.lua
-- @param keep_open (bool) If the panel is currently open
-- perform a no-op, useful when the caller only wants to
-- confirm the panel is present.
-- @param cycle (bool) Close and open the panel in succession,
-- useful when resizing events occur to snap the panel back
-- into the appropriate size.
function M.toggle_panel(state, keep_open, cycle, close)
local cur_win = vim.api.nvim_get_current_win()
-- if state is nil then try to grab it from current tab.
if state == nil then
local cur_tab = vim.api.nvim_get_current_tabpage()
state = lib_state.get_state(cur_tab)
if state == nil then
lib_notify.notify_popup_with_timeout("Must open a litee component before toggling the panel.", 1750, "error")
return
end
end
local component_open = false
local component_cursors = {}
for component, _ in pairs(components) do
if
state[component] ~= nil
and state[component].win ~= nil
and vim.api.nvim_win_is_valid(state[component].win)
then
component_open = true
component_cursors[component] = vim.api.nvim_win_get_cursor(state[component].win)
end
end
-- components are open, close them, recording its dimensions
-- for proper restore.
if
not keep_open or
cycle or
close
then
if component_open then
for component, _ in pairs(components) do
if
state[component] ~= nil
and state[component].win ~= nil
and vim.api.nvim_win_is_valid(state[component].win)
then
state[component].win_dimensions = {
height = vim.api.nvim_win_get_height(state[component].win),
width = vim.api.nvim_win_get_width(state[component].win)
}
vim.api.nvim_win_close(state[component].win, true)
end
end
if cycle then
M.toggle_panel(state, false, false)
end
return
end
end
for component, callbacks in pairs(components) do
if state[component] ~= nil then
if callbacks.pre(state) then
M._open_window(component, state)
-- restore cursor positions if possible.
if component_cursors[component] ~= nil then
lib_util.safe_cursor_reset(
state[component].win,
component_cursors[component]
)
end
end
end
end
vim.api.nvim_set_current_win(cur_win)
end
-- Similar to toggle_panel but retrieves state from
-- Neovim's current context.
function M.toggle_panel_ctx(keep_open, cycle)
local state = lib_state.get_state_ctx()
if state == nil then
return
end
M.toggle_panel(state, keep_open, cycle)
end
-- realize_current_and_desired will determine the current and desired layouts
-- given a requested window type to open.
--
-- @param check_component (string) The identifier of a registered component
-- which is checked against the current layout.
-- @param desired_compoent The identifier of a registered component which is
-- the desired component to add/display in the panel.
-- @param current_layout (list of int) A list of window ids currently comprising
-- any open panel components.
-- @param desired_layout (list of string) A list of panel components desired to be
-- displayed.
-- @param state (table) A table of state as defined by lib/state.lua
--
-- @returns continue (bool) A bool which informs the caller to stop processing
-- the declarative evaluation of the panel layout. This occurs if the desired
-- component is already being displayed.
local function realize_current_and_desired(check_component, desired_component, current_layout, desired_layout, state)
local current_tabpage = vim.api.nvim_win_get_tabpage(
vim.api.nvim_get_current_win()
)
-- check if the desired window exists.
if
state[check_component] == nil or
state[check_component].win == nil or
(not vim.api.nvim_win_is_valid(state[check_component].win))
then
if check_component == desired_component then
-- checked window is invalid, its also the desired window to open
-- add it to desired layout.
table.insert(desired_layout, desired_component)
end
return true
end
local win = state[check_component].win
local win_tabpage = vim.api.nvim_win_get_tabpage(win)
-- desired window type is already open on current tabpage, noop
if check_component == desired_component and current_tabpage == win_tabpage then
return false
end
-- the checked window type is open and on the current tab, unconditionally add it to
-- desired_layout to preserve it existence.
if current_tabpage == win_tabpage then
table.insert(current_layout, win)
table.insert(desired_layout, check_component)
-- the window type we are checking is also the desired window type, but its
-- opened in another tab, close it and add desired window type to desired layout.
-- it will be opened on the current tab.
elseif check_component == desired_component then
vim.api.nvim_win_close(win, true)
table.insert(desired_layout, desired_component)
end
return true
end
-- open_window will first compute the current UI layout
-- then compute the desired layout and finally call
-- _setup_window with both.
--
-- @param desired_component (string) The registered component
-- which should be opened in the panel.
--
-- @param state (table) The state table as defined by
-- lib/state.lua
function M._open_window(desired_component, state)
-- holds open window handles we'll reuse
local current_layout = {}
-- holds the window types to ensure present in the ui
local desired_layout = {}
for check_component, _ in pairs(components) do
if not realize_current_and_desired(check_component, desired_component, current_layout, desired_layout, state) then
return
end
end
M._setup_window(current_layout, desired_layout, state)
end
-- open_to opens the panel and moves the cursor to the requested
-- component.
--
-- if open_to is called when nvim is focused inside
-- the component the focus will be switched back to the window the ui
-- was invoked from.
--
-- @param component (string) The component to open to.
-- @param state (table) The state table as defined by
-- lib/state.lua
-- @return opened (bool) Whether the panel has been open.
function M.open_to(component, state)
if state[component] == nil then
-- notify.notify_popup_with_timeout("Cannot toggle panel until ..", 1750, "error")
return false
end
local current_win = vim.api.nvim_get_current_win()
if current_win == state[component].win then
vim.api.nvim_set_current_win(state[component].invoking_win)
return
end
if
state[component].win ~= nil
and vim.api.nvim_win_is_valid(state[component].win)
then
vim.api.nvim_set_current_win(state[component].win)
return
end
M.toggle_panel(state, true, false)
vim.api.nvim_set_current_win(state[component].win)
end
M.popout_panel_state = nil
function M.close_current_popout()
if M.popout_panel_state == nil then
return
end
if vim.api.nvim_win_is_valid(M.popout_panel_state.float_win) then
vim.api.nvim_win_close(M.popout_panel_state.float_win, true)
-- if panel was closed when popout panel was created, close it again,
-- otherwise open it again.
if M.popout_panel_state.panel_open then
-- issue a cycle here to reset any odd spacing from removing the window
-- from the panel.
M.toggle_panel(M.popout_panel_state.litee_state, false, true, false)
end
end
M.popout_panel_state = nil
end
-- popout_to will pop the requested component
-- out of the panel to a popup on the bottom right
-- hand of the editor and focus the popup window.
--
-- if the panel is open when a call to "popout_to" is made,
-- when the popup is closed via "close_current_popout()",
-- or a jump is made with lib/jump functions,
-- then the window will be popped back into the panel.
--
-- if the panel was closed when a call to "popup_to" was made
-- this is remembered, and the panel will remain closed after
-- "close_current_popup()" is called or a jump is made with
-- lib/jump functions.
--
-- @param component (string) The registered component name
-- to create the popout for.
-- @param state (table) The current global state as defined
-- in lib/state
-- @param before_focus A callback ran just before switching
-- focus to the popout floating win. This callback is ran
-- in the original win when the call to popout_to was made.
-- @param after_focus Same as "before_focus" but runs inside
-- the newly created popout floating win.
function M.popout_to(component, state, before_focus, after_focus)
if
state == nil
or state[component] == nil
or components[component] == nil
then
return
end
-- close any popup which maybe open.
M.close_current_popout()
-- reset the popout panel state
M.popout_panel_state = {}
-- check panel open state so we can restore it correctly
-- on close_current_popout()
M.popout_panel_state.panel_open = M.is_panel_open(state)
local popup_conf = {
relative = "editor",
anchor = "NW",
width = math.floor(vim.opt.columns:get()/2),
height = math.floor(vim.opt.lines:get()/2),
focusable = true,
zindex = 98,
border = "rounded",
row = math.floor(vim.opt.lines:get() - (vim.opt.cmdheight:get() + 1)/2),
col = math.floor(vim.opt.columns:get()/2),
}
if M.popout_panel_state.panel_open
and state[component].win ~= nil
and vim.api.nvim_win_is_valid(state[component].win)
then
-- if panel is open and win is valid make it a float.
vim.api.nvim_win_set_config(state[component].win, popup_conf)
M.popout_panel_state.float_win = state[component].win
M.popout_panel_state.litee_state = state
else
-- if the panel is closed or win is not valid
-- manually create the floating window.
components[component].pre(state)
M.popout_panel_state.float_win = vim.api.nvim_open_win(state[component].buf, false, popup_conf)
state[component].win = M.popout_panel_state.float_win
M._set_win_opts(M.popout_panel_state.float_win)
M.popout_panel_state.litee_state = state
vim.api.nvim_win_set_buf(M.popout_panel_state.float_win, state[component].buf)
end
-- run callback before focusing the callback window
if before_focus ~= nil then
before_focus(true)
end
-- set focus into float
vim.api.nvim_set_current_win(M.popout_panel_state.float_win)
-- if we created a floating win, we need to run post callbacks
-- when inside of it.
components[component].post(state)
-- run callback after focusing the callback window
if after_focus ~= nil then
after_focus()
end
end
-- setup_window evaluates the current layout and the desired layout
-- and opens the necessary windows to obtain the desired layout.
--
-- @param current_layout (list of int) A list of win handles comprising
-- the currently opened panel windows. If present, they will be reused
-- to realize the desired_layout.
-- @param desired_layout (list of string) A list of components desired
-- to be opened and present in the panel.
-- @param state (table) The state table as defined by
-- lib/state.lua
function M._setup_window(current_layout, desired_layout, state)
for i, component in ipairs(desired_layout) do
local buffer_to_set = nil
local dimensions_to_set = nil
buffer_to_set = state[component].buf
if state[component].win_dimensions ~= nil then
dimensions_to_set = state[component].win_dimensions
end
-- we can reuse the current layout windows
if i <= #current_layout then
vim.api.nvim_win_set_buf(current_layout[i], buffer_to_set)
state[component].win = current_layout[i]
vim.api.nvim_set_current_win(state[component].win)
goto continue
end
-- we are out of open windows, so we need to create some
if #current_layout == 0 then
-- there is no current layout, so just do a botright or equivalent
if config["panel"].orientation == "left" then
vim.cmd("topleft vsplit")
vim.cmd("vertical resize " ..
config["panel"].panel_size)
elseif config["panel"].orientation == "right" then
vim.cmd("botright vsplit")
vim.cmd("vertical resize " ..
config["panel"].panel_size)
elseif config["panel"].orientation == "top" then
vim.cmd("topleft split")
vim.cmd("resize " ..
config["panel"].panel_size)
elseif config["panel"].orientation == "bottom" then
vim.cmd("botright split")
vim.cmd("resize " ..
config["panel"].panel_size)
end
goto set
end
-- cursor currently in a reused window, split according to layout config
if config["panel"].orientation == "left" then
vim.cmd("below split")
elseif config["panel"].orientation == "right" then
vim.cmd("below split")
elseif config["panel"].orientation == "top" then
vim.cmd("vsplit")
elseif config["panel"].orientation == "bottom" then
vim.cmd("vsplit")
end
::set::
cur_win = vim.api.nvim_get_current_win()
state[component].win = cur_win
vim.api.nvim_win_set_buf(state[component].win, buffer_to_set)
M._set_win_opts(state[component].win)
if
dimensions_to_set ~= nil and
dimensions_to_set.width ~= nil and
dimensions_to_set.height ~= nil
then
if (config["panel"].orientation == "left" or config["panel"].orientation == "right") then
vim.api.nvim_win_set_width(cur_win, dimensions_to_set.width)
else
vim.api.nvim_win_set_height(cur_win, dimensions_to_set.height)
end
else
dimensions_to_set = {}
dimensions_to_set.height = vim.api.nvim_win_get_height(cur_win)
dimensions_to_set.wdith = vim.api.nvim_win_get_width(cur_win)
state[component].dimensions = dimensions_to_set
end
::continue::
if components[component].post ~= nil then
components[component].post(state)
end
end
end
-- _set_win_opts sets the manditory window options for a
-- panel window.
-- @param win (int) Window ID of panel window
function M._set_win_opts(win)
vim.api.nvim_win_set_option(win, 'number', false)
vim.api.nvim_win_set_option(win, 'cursorline', true)
vim.api.nvim_win_set_option(win, 'relativenumber', false)
vim.api.nvim_win_set_option(win, 'signcolumn', 'no')
vim.api.nvim_win_set_option(win, 'wrap', false)
vim.api.nvim_win_set_option(win, 'winfixwidth', true)
vim.api.nvim_win_set_option(win, 'winfixheight', true)
vim.api.nvim_win_set_option(win, 'winhighlight', 'Normal:NormalSB')
end
return M
================================================
FILE: lua/litee/lib/state/autocmds.lua
================================================
local lib_tree = require('litee.lib.tree')
local lib_state = require('litee.lib.state')
local M = {}
M.on_tab_closed = function(tab)
local state = lib_state.get_state(tab)
if state == nil then
return
end
for _, s in pairs(state) do
if s ~= nil then
lib_tree.remove_tree(s.tree)
end
end
lib_state.put_state(tab, nil)
end
return M
================================================
FILE: lua/litee/lib/state/init.lua
================================================
local M = {}
-- registry is a per-tab-page registry for
-- state.
--
-- state defines information necessary for
-- a UI component to be used with litee-lib.
--
-- the state structure is as follows:
-- "component" = {
-- buf = 0,
-- win = 0,
-- tab = 0,
-- tree = 0,
-- win_dimensions = {0, 0},
-- invoking_win = 0,
-- active_lsp_clients = {list of lsp clients},
-- (component specific fields)...
-- }
--
-- a component using the registry should consider
-- only it's portion of the state as writable but
-- is free to pass the full state object to other
-- lib methods.
M.registry = {}
-- get_state returns the state for the given tab
-- @param tab (int) The tab ID to retrieve state for.
-- @returns state (table) The state as defined by this
-- module.
function M.get_state(tab)
return M.registry[tab]
end
-- get_state_ctx is similiar to get_state but obtains
-- the tab ID from the current Neovim context.
function M.get_state_ctx()
local win = vim.api.nvim_get_current_win()
local tab = vim.api.nvim_win_get_tabpage(win)
local state = M.registry[tab]
return state
end
-- get_component_state returns the state for a
-- particular component that's utilizing lib state.
--
-- @param tab (int) The tab ID to retrieve state for.
-- @param component (string) The name of the component
-- storing its state.
function M.get_component_state(tab, component)
local s = M.registry[tab]
if s == nil then return nil end
return s[component]
end
-- put_state writes a state table to the registry for
-- later retrieval.
--
-- this method overwrites all current state so use with
-- caution.
-- @param tab (int) The tab ID to associate the provided state
-- with
-- @param state (table) The state as defined by this
-- module.
-- @returns state (table) The updated state table as defined
-- by this module
function M.put_state(tab, state)
M.registry[tab] = state
return M.registry[tab]
end
-- put_component_state will store the component state for
-- later retrieval.
--
-- @param tab (int) The tab ID to associate the provided state
-- with
-- @param component (string) The name of the component
-- storing its state table.
-- @param state (table) The state as defined by this
-- module. This table should not include the component
-- name as a leading key.
-- @returns state (table) The updated state table as defined
-- by this module
function M.put_component_state(tab, component, state)
local s = M.registry[tab]
if s == nil then
s = {}
M.registry[tab] = s
end
s[component] = state
return s
end
function M.get_active_components(tab)
local s = M.registry[tab]
local components = {}
for component, _ in pairs(s) do
table.insert(components, component)
end
return components
end
function M.get_type_from_buf(tab, buf)
local state = M.registry[tab]
if state == nil then
return nil
end
for component, s in pairs(state) do
if buf == s.buf then
return component
end
end
return nil
end
M.get_tree_from_buf = function(tab, buf)
local state = M.registry[tab]
if state == nil then
return nil
end
local type = M.get_type_from_buf(tab, buf)
if type == nil then
return nil
end
return state[type].tree
end
return M
================================================
FILE: lua/litee/lib/term/init.lua
================================================
local lib_panel = require('litee.lib.panel')
local lib_state = require('litee.lib.state')
local lib_util_buf = require('litee.lib.util.buffer')
local config = require('litee.lib.config').config["term"]
local M = {}
local opts = {noremap = true, silent=true}
local function terminal_buf_setup(buf)
vim.api.nvim_buf_set_keymap(buf, 't', "<C-w>v", "<cmd>lua require('configs.terminal').terminal_vsplit()<cr>", opts)
vim.api.nvim_buf_set_keymap(buf, 't', "<C-w>n", "<C-\\><C-n>", opts)
vim.api.nvim_buf_set_keymap(buf, 't', "<C-w>h", "<C-\\><C-n><C-w>h", opts)
vim.api.nvim_buf_set_keymap(buf, 't', "<C-w>j", "<C-\\><C-n><C-w>j", opts)
vim.api.nvim_buf_set_keymap(buf, 't', "<C-w>k", "<C-\\><C-n><C-w>k", opts)
vim.api.nvim_buf_set_keymap(buf, 't', "<C-w>l", "<C-\\><C-n><C-w>l", opts)
vim.api.nvim_buf_set_option(buf, 'bufhidden', 'hide')
if config.map_resize_keys then
lib_util_buf.map_resize_keys(config.position, buf, opts)
end
end
local function terminal_win_setup(win)
vim.api.nvim_win_set_option(win, 'winfixheight', true)
end
-- terminal opens a native Neovim terminal instance that
-- is aware of the litee.panel layout.
--
-- the terminal will not truncate the panel if it is open
-- on the left or right position.
--
-- the terminal can be opened at the top of bottom of the
-- editor, but not left or right and this is configured
-- via the 'litee.lib.config.term["position"]' stanza.
--
-- the terminal has a fixed height so a call to <C-w>=
-- does not effect the height and only evenly spaces
-- any vsplit terminals in the pane.
--
-- the $SHELL environment variable must be set correctly
-- to open the appropriate shell.
function M.terminal()
local shell = vim.fn.getenv('SHELL')
if shell == nil or shell == "" then
return
end
local buf = vim.api.nvim_create_buf(false, false)
if buf == 0 then
vim.api.nvim_err_writeln("failed to create terminal buffer")
return
end
terminal_buf_setup(buf)
if config.position == "top" then
vim.cmd('topleft split')
else
vim.cmd('botright split')
end
vim.cmd("resize " .. config.term_size)
terminal_win_setup(vim.api.nvim_get_current_win())
local cur_win = vim.api.nvim_get_current_win()
local cur_tab = vim.api.nvim_get_current_tabpage()
local state = lib_state.get_state(cur_tab)
vim.api.nvim_win_set_buf(cur_win, buf)
vim.fn.termopen(shell)
if state ~= nil then
if lib_panel.is_panel_open(state) then
lib_panel.toggle_panel_ctx(true, true)
end
end
vim.api.nvim_set_current_win(cur_win)
end
function M.terminal_vsplit()
local shell = vim.fn.getenv('SHELL')
if shell == nil or shell == "" then
return
end
local buf = vim.api.nvim_create_buf(false, false)
if buf == 0 then
vim.api.nvim_err_writeln("failed to create terminal buffer")
return
end
vim.cmd('vsplit')
local cur_win = vim.api.nvim_get_current_win()
terminal_win_setup(cur_win)
vim.api.nvim_win_set_buf(cur_win, buf)
vim.fn.termopen(shell)
terminal_buf_setup(buf)
end
function M.list_terminals()
local terms = {}
for _, b in ipairs(vim.api.nvim_list_bufs()) do
local buf_name = vim.api.nvim_buf_get_name(b)
if vim.fn.match(buf_name, "term://") == 0 then
table.insert(terms, {name = buf_name, buf = b})
end
end
if #terms == 0 then
return
end
vim.ui.select(
terms,
{
prompt = "select a terminal to display",
format_item = function(item)
return item["name"] .. " " .. item["buf"]
end
},
function (choice)
-- first see if there's a window that's opened with this term
for _, w in ipairs(vim.api.nvim_list_wins()) do
if vim.api.nvim_win_get_buf(w) == choice["buf"] then
vim.api.nvim_set_current_win(w)
return
end
end
if config.position == "top" then
vim.cmd('topleft split')
else
vim.cmd('botright split')
end
vim.cmd("resize " .. config.term_size)
local cur_win = vim.api.nvim_get_current_win()
local cur_tab = vim.api.nvim_get_current_tabpage()
local state = lib_state.get_state(cur_tab)
vim.api.nvim_win_set_buf(cur_win, choice["buf"])
if state ~= nil then
if lib_panel.is_panel_open(state) then
lib_panel.toggle_panel_ctx(true, true)
end
end
vim.api.nvim_set_current_win(cur_win)
end
)
end
return M
================================================
FILE: lua/litee/lib/tree/init.lua
================================================
local lib_marshal = require('litee.lib.tree.marshal')
local M = {}
-- registry keeps a mapping between tree handles
-- and a tree table.
--
-- the registry and tree table stricture is defined
-- below:
-- {
-- 1: {root = {}, depth_table = {}, kind = "", source_line_map = nil, buf_line_map = nil}
-- }
-- 1 is the the associated handle returned by new_tree
-- root is the root node of the tree
--
-- depth_table is the depth table associated with the tree
--
-- kind is the kind of tree and will typically be a component's
-- name as defined in lib/state.lua
local registry = {}
-- new_tree registers an empty tree and returns
-- the handle to the caller.
--
-- @param kind (string) A string representing, at
-- a high level, the kind of tree being created.
-- @returns tree_handle (int) A handle representing
-- the created tree.
function M.new_tree(kind)
local handle = #registry+1
registry[handle] = {root = {}, depth_table = {}, kind = kind}
return handle
end
-- get_tree takes a call handle and returns a table
-- representing our tree components.
--
-- @param handle (int) A tree handle ID previously
-- returned from a call to new_tree
-- @returns tree (table) A tree table as defined
-- in this module, see "registry" comments.
function M.get_tree(handle)
local t = registry[handle]
-- attach marshaler info to tree if it exists.
if t ~= nil then
t.source_line_map = lib_marshal.source_line_map[handle]
t.buf_line_map = lib_marshal.buf_line_map[handle]
end
return registry[handle]
end
-- remove_tree takes a call handle and removes
-- the associated tree from memory.
--
-- @param handle (int) A tree handle ID previously
-- returned from a call to new_tree
function M.remove_tree(handle)
if handle == nil then
return
end
if registry[handle] == nil then
return
end
registry[handle] = nil
end
-- search_dpt will search the dpt at the given
-- depth for the given key.
--
-- @param dpt (table) A depth table retrieved
-- from a previous call to get_tree.
-- @param depth (int) The depth at which to search
-- for the key
-- @param key (string) The node's key to search
-- for
-- @returns node (table) If found, a node table
-- as defined in lib/tree/node.lua. Nil if search
-- fails.
function M.search_dpt(dpt, depth, key)
local nodes = dpt[depth]
if nodes == nil then
return nil
end
for _, node in ipairs(nodes) do
if node.key == key then
return node
end
end
return nil
end
-- recursive_dpt_compute traverses the tree
-- and flattens it into out depth_table
--
-- @param node (table) A node table
-- as defined in lib/tree/node.lua
-- @param handle (int) A tree handle ID previously
-- returned from a call to new_tree
local function _recursive_dpt_compute(handle, node)
local depth = node.depth
if registry[handle].depth_table[depth] == nil then
registry[handle].depth_table[depth] = {}
end
table.insert(registry[handle].depth_table[depth], node)
-- recurse
for _, child in ipairs(node.children) do
_recursive_dpt_compute(handle, child)
end
end
-- _refresh_dpt dumps the current depth_table
-- and writes a new one.
--
-- @param handle (int) A tree handle ID previously
-- returned from a call to new_tree
local function _refresh_dpt(handle)
registry[handle].depth_table = {}
_recursive_dpt_compute(handle, registry[handle].root)
end
-- add_node will add a node to the tree pointed to by the
-- provided tree handle.
--
-- @param handle (int) A tree handle ID previously
-- returned from a call to new_tree
-- @param parent (table) The parent node table of the subtree being
-- added. the parent's "depth" field is significant. setting it to
-- 0 will throw away the current root and create a new tree.
-- See usages of this function to understand how depth can be safely
-- set.
-- @param children (table) The children of the parent.
-- the child's depth field has no significant and can be computed
-- from the parents (unless external is used, see below).
-- @param external (bool) If true an entire tree has been built externally
-- and the root will be added to the tree without any modifications.
-- the children param has no significance in this scenario.
-- note: when using external the calling code must set the all parent
-- and children depth fields correctly.
function M.add_node(handle, parent, children, external)
if registry[handle] == nil then
return
end
-- external nodes are roots of trees built externally
-- if this is true set the tree's root to the incoming parent
-- and immediately return
if external then
registry[handle].root = parent
_refresh_dpt(handle)
return
end
-- if depth is 0 we are creating a new call tree.
if parent.depth == 0 then
registry[handle].root = parent
registry[handle].expanded = true
registry[handle].depth_table = {}
registry[handle].depth_table[0] = {}
table.insert(registry[handle].depth_table[0], registry[handle].root)
end
-- if parent's depth doesn't exist we can't
-- continue.
if registry[handle].depth_table[parent.depth] == nil then
-- this is an error
return
end
-- lookup parent node in depth tree (faster then tree diving.)
local pNode = nil
for _, node in pairs(registry[handle].depth_table[parent.depth]) do
if node.key == parent.key then
pNode = node
break
end
end
if pNode == nil then
return
end
-- ditch the old child array, we are refreshing the children
pNode.children = {}
local child_depth = parent.depth + 1
for _, child in ipairs(children) do
child.depth = child_depth
table.insert(pNode.children, child)
end
_refresh_dpt(handle)
end
-- remove subtree will remove the subtree of the
-- provided node, leaving the root node present.
--
-- @param node (table) A node table
-- as defined in lib/tree/node.lua. This node must
-- be the root of the subtree being removed.
-- @param root (bool) Should be "true", indicator that
-- recursion is at the root node.
function M.remove_subtree(tree, node, root)
-- recurse to leafs
for _, child in ipairs(node.children) do
M.remove_subtree(tree, child, false)
end
if not root then
-- remove yourself from the depth table
local dt = registry[tree].depth_table[node.depth]
local nw_dt = {}
for _, dt_node in ipairs(dt) do
if dt_node.key ~= node.key then
table.insert(nw_dt, dt_node)
end
end
registry[tree].depth_table[node.depth] = nw_dt
end
if root then
-- remove your children
node.children = {}
end
end
-- collapse subtree will recursively collapse the
-- subtree starting at root and inclusive to root.
--
-- this function does not modify the tree structure
-- like "remove_subtree" but rather sets all nodes in the
-- subtree to expanded = false. on next marshal of the
-- tree the nodes will be collapsed.
--
-- @param node (table) A node table
-- as defined in lib/tree/node.lua, this node must be
-- the root of the subtree to be collapsed.
function M.collapse_subtree(root)
root.expanded = false
for _, child in ipairs(root.children) do
M.collapse_subtree(child)
end
end
-- reparent_node will make the provided node
-- the root of the provided tree, discarding
-- any ancestors above it in the process.
--
-- @param handle (int) A tree handle ID previously
-- returned from a call to new_tree
--
-- @param depth (int) The depth at which to search
-- for the key
-- @param node (table) A node table
-- as defined in lib/tree/node.lua, this node must be
-- the desired new root of the tree.
function M.reparent_node(handle, depth, node)
-- we are the new root, dump the current root_node and
-- set yourself
if depth == 0 then
registry[handle].root = node
registry[handle].root.depth = 0
end
-- recurse to leafs
for _, child in ipairs(node.children) do
M.reparent_node(handle, depth+1, child)
-- recursion done, update your depth
child.depth = depth+1
end
if depth == 0 then
-- we are the root node, refresh depth_table with
-- new tree.
_refresh_dpt(handle)
end
end
-- dump_tree will dump the tree data structure to a
-- buffer for debugging.
--
-- @param node (table) A node table
-- as defined in lib/tree/node.lua, this node can be
-- a root or a child depending on where in the tree
-- you'd like to dump.
function M.dump_tree(node)
local buf = vim.api.nvim_create_buf(true, false)
vim.api.nvim_buf_set_name(buf, "calltree-dump")
vim.cmd("botright split")
local win = vim.api.nvim_get_current_win()
local lines = vim.fn.split(vim.inspect(node), "\n")
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
vim.api.nvim_win_set_buf(win, buf)
vim.api.nvim_set_current_win(win)
end
-- write_tree recursively marshals all nodes from the provided root
-- down, into UI lines and writes those lines to the provided buffer.
--
-- @param buf (int) The buffer handle to write the marshalled tree to
-- @param tree (int) The tree handle to marshal into the provided buffer
-- @param marshal_func (function(node)) A function
-- when given a node returns the following strings
-- name: the display name for the node
-- detail: details to display about the node
-- icon: any icon associated with the node
function M.write_tree(buf, tree, marshal_func, no_guide_leaf)
local root = registry[tree].root
if root == nil then
return
end
lib_marshal._marshal_tree(buf, {}, root, tree, {}, marshal_func, no_guide_leaf)
return buf
end
-- same as `write_tree` but forces `no_guide_leaf` to be true
-- on each call.
--
-- useful for clients of the lib which always choose this option.
function M.write_tree_no_guide_leaf(buf, tree, marshal_func)
M.write_tree(buf, tree, marshal_func, true)
end
-- marshal_line takes a UI buffer line and
-- marshals it into a tree.Node.
--
-- @param linenr (table) A buffer line as returned by
-- vim.api.nvim_win_get_cursor()
-- @param handle (int) A tree handle ID previously
-- returned from a call to new_tree
--
-- @param node (table) A node table
-- as defined in lib/tree/node.lua, nil if
-- marshal failed.
function M.marshal_line(linenr, handle)
return lib_marshal.marshal_line(linenr, handle)
end
function M.flatten_depth_table(dtp)
local nodes = {}
for _, depth in pairs(dtp) do
for _, node in ipairs(depth) do
table.insert(nodes, node)
end
end
return nodes
end
return M
================================================
FILE: lua/litee/lib/tree/marshal/init.lua
================================================
local lib_util = require('litee.lib.util')
local lib_hi = require('litee.lib.highlights')
local lib_tree_config = require('litee.lib.config').config["tree"]
local icon_set = require('litee.lib').icon_set
local M = {}
-- buf_line_map keeps a mapping between marshaled
-- buffer lines and the node objects for a given tree.
--
-- the structure of this table is as follows:
-- {
-- tree_handle = {
-- line_number = node,
-- ...
-- },
-- ...
-- }
M.buf_line_map = {}
-- maps source code lines to buffer lines
-- for a given tree.
--
-- currently only useful for symboltree.
--
-- the struture of this table is as follows:
-- {
-- tree_handle = {
-- source_file_line_number = {
-- uri = relative_path_to_file,
-- line = calltree_buffer_line_number
-- }
-- ...
-- }
-- ...
-- }
M.source_line_map = {}
-- marshal_node takes a node and a marshal_func and
-- produces a buffer line.
--
-- @param node (table) A node table
-- as defined in lib/tree/node.lua, this node must
-- be the node being marshalled into a line.
-- @param marshal_func (function(node)) A function
-- when given a node returns the following strings
-- name: the display name for the node
-- detail: details to display about the node
-- icon: any icon associated with the node
-- expand_guide: optional override when the marshal
-- func want to specify a specific expand guide.
-- @param no_guide_leaf (bool) If node is a leaf
-- node and this bool is true an expansion guide
-- will be ommited from the rendered line.
-- @return buffer_line (string) a string with
-- the preamble of the buffer line.
-- @return virt_text (table) A table suitable for
-- configuring virtual text containing the details
-- of the node. This virtual text will be placed EOL
-- in the buffer line.
function M.marshal_node(node, marshal_func, no_guide_leaf)
local expand_guide = ""
if node.expanded then
expand_guide = icon_set["Expanded"]
else
expand_guide = icon_set["Collapsed"]
end
if no_guide_leaf
and #node.children == 0
and node.expanded == true
then
expand_guide = icon_set["Space"]
end
local str = ""
local name, detail, icon, expand_guide_override = marshal_func(node)
if expand_guide_override ~= nil then
expand_guide = expand_guide_override
end
if lib_tree_config.indent_guides then
for i=1, node.depth do
if i == 1 then
str = str .. icon_set["Space"]
else
str = str .. icon_set["IndentGuide"] .. icon_set["Space"]
end
end
else
for _=1, node.depth do
str = str .. icon_set["Space"]
end
end
if icon == nil then
icon = " "
end
-- ▶ Func1
str = str .. expand_guide .. icon_set["Space"]
str = str .. icon .. icon_set["Space"] .. icon_set["Space"] .. name
-- return detail as virtual text chunk.
return str, {{detail, lib_hi.hls.SymbolDetailHL}}
end
-- marshal_line takes a UI buffer line and
-- marshals it into a tree.Node.
--
-- @param linenr (table) A buffer line as returned by
-- vim.api.nvim_win_get_cursor()
-- @param handle (int) A tree handle ID previously
-- returned from a call to new_tree
--
-- @param node (table) A node table
-- as defined in lib/tree/node.lua, nil if
-- marshal failed.
function M.marshal_line(linenr, handle)
if M.buf_line_map == nil then
return nil
end
if M.buf_line_map[handle] == nil then
return nil
end
local node = M.buf_line_map[handle][linenr[1]]
return node
end
-- marshal_tree recursively marshals all nodes from the provided root
-- down, into UI lines.
--
-- @param buf (int) The buffer handle to write the marshalled tree to
-- @param lines (list of string) Recursive accumlator of marshaled
-- lines. Start this function with an empty table.
-- @param node (table) A node table as defined in lib/tree/node.lua,
-- this node must be the root of the tree being marshalled into buffer lines.
-- @param virtual_text_lines (list of string) Recursive accumlator of marshaled
-- virtual text lines. Start this function with an empty table.
-- @param marshal_func (function(node)) A function
-- when given a node returns the following strings
-- name: the display name for the node
-- detail: details to display about the node
-- icon: any icon associated with the node
function M._marshal_tree(buf, lines, node, tree, virtual_text_lines, marshal_func, no_guide_leaf)
if node.depth == 0 then
virtual_text_lines = {}
-- create a new line mapping
M.buf_line_map[tree] = {}
M.source_line_map[tree] = {}
end
local line, virtual_text = M.marshal_node(node, marshal_func, no_guide_leaf)
table.insert(lines, line)
table.insert(virtual_text_lines, virtual_text)
M.buf_line_map[tree][#lines] = node
-- if the node has a location we can track where it
-- exists in the source code file.
if node.location ~= nil and not vim.tbl_isempty(node.location) then
local start_line = node.location["range"]["start"].line
M.source_line_map[tree][start_line+1] = {
uri = lib_util.absolute_path_from_uri(node.location.uri),
line = #lines
}
end
-- if we are an expanded node or we are the root (always expand)
-- recurse
if node.expanded or node.depth == 0 then
for _, child in ipairs(node.children) do
M._marshal_tree(buf, lines, child, tree, virtual_text_lines, marshal_func, no_guide_leaf)
end
end
-- we are back at the root, all lines are inserted, lets write it out
-- to the buffer
if node.depth == 0 then
vim.api.nvim_buf_set_option(buf, 'modifiable', true)
vim.api.nvim_buf_set_lines(buf, 0, -1, true, {})
vim.api.nvim_buf_set_lines(buf, 0, #lines, false, lines)
vim.api.nvim_buf_set_option(buf, 'modifiable', false)
for i, vt in ipairs(virtual_text_lines) do
if vt[1][1] == "" then
goto continue
end
local opts = {
virt_text = vt,
virt_text_pos = 'eol',
hl_mode = 'combine'
}
vim.api.nvim_buf_set_extmark(buf, 1, i-1, 0, opts)
::continue::
end
end
end
return M
================================================
FILE: lua/litee/lib/tree/node.lua
================================================
local M = {}
-- new_node creates a polymorphic node
-- which the caller can attach their own
-- node specific fields to.
--
-- a unique key must be generated and provided
-- along with the desired depth of the node with
-- the tree it will belong in.
--
-- see function definition for node field documentation.
-- only the requird fields are present in the constructor
-- optional fields can be added on to the node by the caller.
--
-- @param name (string) a non-unique display name
-- for the node.
-- @param key (string) a unique key used to identify
-- the node when placed into a tree.
-- @param depth (int) the node's depth in the tree, zero
-- indexed. depth 0 indicates this node is the root of
-- the tree.
-- @returns (table) A table representing a node object.
-- the caller is free to attach domain specific fields
-- that convey the node's purpose othorgonal to the usage
-- in the tree.
function M.new_node(name, key, depth)
return {
-- a non-unique display name for the node
name = name,
-- the depth of the node in the target tree
depth = depth,
-- a unique key used to identify the node when
-- placed into a tree
key = key,
-- a list of children nodes with recursive definitions.
children = {},
-- a "Location" object as defined by the LSP which
-- associates this node with a source code file and line range.
-- see:
-- https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#location
location = {},
-- a list of "Range" objects, relative to the above location object,
-- which relate in a domain specific way to this node.
-- see:
-- https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#range
references = {},
-- whether this node is expanded in its containing
-- tree.
expanded = false,
offset_encoding = "utf-8"
}
end
return M
================================================
FILE: lua/litee/lib/util/buffer.lua
================================================
local M = {}
local original_guicursor = ""
-- hide_cursor will dynamically create the LTCursorHide hi and
-- set the guicursor option to this hi group.
--
-- the LTCursorHide hi has the same bg/fg as the CursorLine hi which
-- is used inside the LITEE.nvim windows.
--
-- this effectively hides the cursor by making it blend into the cursor
-- line.
--
-- hide : bool - if true hides the cursor if false sets the guicursor
-- option back to the value when Neovim started.
function M.hide_cursor(hide)
if original_guicursor == "" then
for _, section in ipairs(vim.opt.guicursor:get()) do
original_guicursor = original_guicursor .. section .. ','
end
end
if not hide then
vim.cmd('set guicursor=' .. original_guicursor)
return
end
local colors_rgb = vim.api.nvim_get_hl_by_name("CursorLine", true)
local colors_256 = vim.api.nvim_get_hl_by_name("CursorLine", false)
local hi = string.format("hi LTCursorHide cterm=None ctermbg=%s ctermfg=%s gui=None guibg=%s guifg=%s",
(function() if colors_256.background ~= nil then return colors_256.background else return "None" end end)(),
(function() if colors_256.foreground ~= nil then return colors_256.foreground else return "None" end end)(),
(function() if colors_rgb.background ~= nil then return string.format("#%x", colors_rgb.background) else return "None" end end)(),
(function() if colors_rgb.foreground ~= nil then return string.format("#%x", colors_rgb.foreground) else return "None" end end)()
)
vim.cmd(hi)
local cursorgui = "set guicursor=n:LTCursorHide"
vim.cmd(cursorgui)
end
-- set_scrolloff will enable a global scrolloff
-- of 999 when set is true.
--
-- when set is false the scrolloff will be set back to 0.
-- useful for keeping the contents of a litee panel centered.
function M.set_scrolloff(set)
if set then
vim.cmd("set scrolloff=999")
else
vim.cmd("set scrolloff=0")
end
end
function M.map_resize_keys(orientation, buffer_handle, opts)
if orientation == "top" then
vim.api.nvim_buf_set_keymap(buffer_handle, "n", "<Right>", ":vert resize +5<cr>", opts)
vim.api.nvim_buf_set_keymap(buffer_handle, "n", "<Left>", ":vert resize -5<cr>", opts)
vim.api.nvim_buf_set_keymap(buffer_handle, "n", "<Up>", ":resize +5<cr>", opts)
vim.api.nvim_buf_set_keymap(buffer_handle, "n", "<Down>", ":resize -5<cr>", opts)
elseif orientation == "bottom" then
vim.api.nvim_buf_set_keymap(buffer_handle, "n", "<Right>", ":vert resize +5<cr>", opts)
vim.api.nvim_buf_set_keymap(buffer_handle, "n", "<Left>", ":vert resize -5<cr>", opts)
vim.api.nvim_buf_set_keymap(buffer_handle, "n", "<Down>", ":resize +5<cr>", opts)
vim.api.nvim_buf_set_keymap(buffer_handle, "n", "<Up>", ":resize -5<cr>", opts)
elseif orientation == "left" then
vim.api.nvim_buf_set_keymap(buffer_handle, "n", "<Up>", ":resize +5<cr>", opts)
vim.api.nvim_buf_set_keymap(buffer_handle, "n", "<Down>", ":resize -5<cr>", opts)
vim.api.nvim_buf_set_keymap(buffer_handle, "n", "<Left>", ":vert resize -5<cr>", opts)
vim.api.nvim_buf_set_keymap(buffer_handle, "n", "<Right>", ":vert resize +5<cr>", opts)
elseif orientation == "right" then
vim.api.nvim_buf_set_keymap(buffer_handle, "n", "<Up>", ":resize +5<cr>", opts)
vim.api.nvim_buf_set_keymap(buffer_handle, "n", "<Down>", ":resize -5<cr>", opts)
vim.api.nvim_buf_set_keymap(buffer_handle, "n", "<Left>", ":vert resize +5<cr>", opts)
vim.api.nvim_buf_set_keymap(buffer_handle, "n", "<Right>", ":vert resize -5<cr>", opts)
end
end
-- a convenience method which will close any popups
-- generated by the litee library (not notifications).
function M.close_all_popups()
require('litee.lib.lsp.hover').close_hover_popup()
require('litee.lib.details').close_details_popup()
end
return M
================================================
FILE: lua/litee/lib/util/init.lua
================================================
local M = {}
function M.absolute_path_from_uri(uri)
local uri_path = vim.fn.substitute(uri, "file://", "", "")
return uri_path
end
-- safe_cursor_reset will attempt to move the
-- cursor to `linenr`, if the provided `linenr`
-- would overflow the buffer the cursor will
-- safely be placed at the lowest available
-- buffer line.
function M.safe_cursor_reset(win, linenr)
if
win == nil
or not vim.api.nvim_win_is_valid(win)
or linenr == nil
then
return
end
local lc = vim.api.nvim_buf_line_count(vim.api.nvim_win_get_buf(win))
if lc < linenr[1] then
linenr[1] = lc
end
vim.api.nvim_win_set_cursor(win, linenr)
end
function M.relative_path_from_uri(uri)
local cwd = vim.fn.getcwd()
local uri_path = vim.fn.substitute(uri, "file://", "", "")
local idx = vim.fn.stridx(uri_path, cwd)
if idx == -1 then
-- we can't resolve a relative path, just give the
-- full path to the file.
return uri_path, false
end
return vim.fn.substitute(uri_path, cwd .. "/", "", ""), true
end
function M.resolve_location(node)
local location = nil
if node.symbol ~= nil then
location = node.symbol.location
elseif node.call_hierarchy_item ~= nil then
location = {
uri = node.call_hierarchy_item.uri,
range = node.call_hierarchy_item.range
}
elseif node.document_symbol ~= nil then
location = {
uri = node.uri,
range = node.document_symbol.selectionRange
}
elseif node.filetree_item ~= nil then
local range = {}
range["start"] = { line = 0, character = 0}
range["end"] = { line = 0, character = 0}
location = {
uri = "file://" .. node.filetree_item.uri,
range = range
}
end
return location
end
function M.resolve_absolute_file_path(node)
if node.symbol ~= nil then
local uri = node.symbol.location.uri
return M.absolute_path_from_uri(uri)
elseif node.call_hierarchy_item ~= nil then
local uri = node.call_hierarchy_item.uri
return M.absolute_path_from_uri(uri)
elseif node.document_symbol ~= nil then
local uri = node.uri
return M.absolute_path_from_uri(uri)
else
return nil
end
end
function M.resolve_hover_params(node)
local params = {}
if node.symbol ~= nil then
params.textDocument = {
uri = node.symbol.location.uri
}
params.position = {
line = node.symbol.location.range.start.line,
character = node.symbol.location.range.start.character
}
elseif node.call_hierarchy_item ~= nil then
params.textDocument = {
uri = node.call_hierarchy_item.uri
}
params.position = {
line = node.call_hierarchy_item.range.start.line,
character = node.call_hierarchy_item.range.start.character
}
elseif node.document_symbol ~= nil then
params.textDocument = {
uri = node.uri
}
params.position = {
line = node.document_symbol.selectionRange.start.line,
character = node.document_symbol.selectionRange.start.character
}
else
return nil
end
return params
end
return M
================================================
FILE: lua/litee/lib/util/path.lua
================================================
local M = {}
-- safe_encode returns a string which is safe to
-- use as a filesystem name.
--
-- this is helpful when encoding filesystem paths
-- to a string that can be used as a filename.
--
-- the encoding is just a sub-set of URL encoding.
--
-- @param str (string) The string to encode.
function M.safe_encode(str)
str, _ = string.gsub(str, '/', '%%2F')
str, _ = string.gsub(str, ' ', '%%20')
str, _ = string.gsub(str, ':', '%%3A')
return str
end
-- decodes a string encoded by safe_encode.
-- see safe_encode for details.
function M.safe_decode(str)
str, _ = string.gsub(str, '%%2F', '/')
str, _ = string.gsub(str, '%%20', ' ')
str, _ = string.gsub(str, '%%3A', ':')
return str
end
function M.is_dir(path)
if vim.fn.isdirectory(path) == 0 then
return false
end
return true
end
function M.file_exists(path)
if vim.fn.filereadable(path) == 0 then
return false
end
return true
end
function M.relative_path_from_uri(uri)
local cwd = vim.fn.getcwd()
local uri_path = vim.fn.substitute(uri, "file://", "", "")
local idx = vim.fn.stridx(uri_path, cwd)
if idx == -1 then
-- we can't resolve a relative path, just give the
-- full path to the file.
return uri_path, false
end
return vim.fn.substitute(uri_path, cwd .. "/", "", ""), true
end
-- provides the filename with no path details for
-- the provided uri.
function M.basename(uri)
local final_sep = vim.fn.strridx(uri, "/")
local uri_len = vim.fn.strlen(uri)
-- if its a dir, remove final "/"
if final_sep+1 == uri_len then
uri = vim.fn.strpart(uri, 0, uri_len-1)
final_sep = vim.fn.strridx(uri, "/")
end
local dir = vim.fn.strpart(uri, final_sep+1, vim.fn.strlen(uri))
return dir
end
function M.parent_dir(path)
local base = M.basename(path)
local diff = vim.fn.strlen(path) - (vim.fn.strlen(base)+1)
local res = vim.fn.strpart(path, 0, diff)
if vim.fn.strridx(path, "/") == #path-1 then
return res
else
return res .. "/"
end
end
function M.path_prefix_match(prefix, path)
local idx = vim.fn.stridx(path, prefix)
if idx == -1 then
return false
end
return true
end
function M.swap_path_prefix(path, old_prefix, new_prefix)
local new_path = vim.fn.substitute(path, old_prefix, new_prefix, "")
return new_path
end
function M.strip_file_prefix(path)
return vim.fn.substitute(path, "file://", "", "")
end
function M.add_file_prefix(path)
vim.fn.substitute(path, "file://", "", "")
return string.format("%s%s", "file://", path)
end
function M.strip_trailing_slash(path)
if vim.fn.strridx(path, "/") == (vim.fn.strlen(path)-1) then
return vim.fn.strpart(path, 0, vim.fn.strlen(path)-1)
end
return path
end
-- strip the prefix from the path, usually to make a
-- relative path, and ensure leading '/'
function M.strip_path_prefix(prefix, path)
local new = vim.fn.substitute(path, prefix, "", "")
if vim.fn.strridx(new, '/') == -1 then
new = '/' .. new
end
return new
end
return M
================================================
FILE: lua/litee/lib/util/window.lua
================================================
local lib_state = require('litee.lib.state')
local lib_tree_config = require('litee.lib.config').config["tree"]
local lib_icons = require('litee.lib.icons')
local lib_hi = require('litee.lib.highlights')
local M = {}
-- a convenience function for setting up tree window highlights
-- suitable for being ran inside a "post_window_create"
-- function.
--
-- see lib.tree.register_component for more details.
function M.set_tree_highlights()
local icon_set = nil
if lib_tree_config.icon_set == nil then
icon_set = lib_icons["default"]
else
icon_set = lib_icons[lib_tree_config.icon_set]
end
-- set configured icon highlights
for icon, hl in pairs(lib_icons.icon_hls) do
vim.cmd(string.format("syn match %s /\\<%s\\>/", hl, icon_set[icon]))
end
-- set configured symbol highlight
vim.cmd(string.format("syn match %s /%s/", lib_hi.hls.SymbolHL, [[\w]]))
vim.cmd(string.format("syn match %s /%s/", lib_hi.hls.SymbolHL, [[\.]]))
vim.cmd(string.format("syn match %s /%s/", lib_hi.hls.SymbolHL, [[\-]]))
vim.cmd(string.format("syn match %s /%s/", lib_hi.hls.SymbolHL, [[\\_]]))
-- set configured expanded indicator highlights
vim.cmd(string.format("syn match %s /\\<%s\\>/", lib_hi.hls.ExpandedGuideHL, icon_set["Expanded"]))
vim.cmd(string.format("syn match %s /\\<%s\\>/", lib_hi.hls.CollapsedGuideHL, icon_set["Collapsed"]))
-- set configured indent guide highlight
vim.cmd(string.format("syn match %s /\\<%s\\>/", lib_hi.hls.IndentGuideHL, icon_set["Guide"]))
end
-- inside_component_win is a helper functions which
-- tells the caller if the currently focused window
-- is a litee.nvim registered component window.
function M.inside_component_win()
local tab = vim.api.nvim_get_current_tabpage()
local win = vim.api.nvim_get_current_win()
local state = lib_state.get_state(tab)
if state == nil then
return false
end
local active_components = lib_state.get_active_components(tab)
local in_litee_panel = false
for _, active_component in ipairs(active_components) do
if win == state[active_component].win then
in_litee_panel = true
end
end
return in_litee_panel
end
-- is_component_win takes a tab and a window and returns
-- true if the provided window is a registered litee panel
-- window and false if not.
--
-- @param tab (int) A tab ID.
-- @param win (int) A window which resides in tab that
-- will be evaluated.
function M.is_component_win(tab, win)
local active_components = lib_state.get_active_components(tab)
local state = lib_state.get_state(tab)
local is_litee_panel = false
for _, active_component in ipairs(active_components) do
if win == state[active_component].win then
is_litee_panel = true
end
end
return is_litee_panel
end
return M
gitextract_v9v813bt/
├── .github/
│ └── FUNDING.yml
├── .gitignore
├── README.md
├── doc/
│ ├── litee.txt
│ └── tags
└── lua/
└── litee/
└── lib/
├── commands.lua
├── config.lua
├── details/
│ └── init.lua
├── highlights/
│ ├── auto.lua
│ └── init.lua
├── icons/
│ └── init.lua
├── init.lua
├── jumps/
│ └── init.lua
├── lsp/
│ ├── hover.lua
│ ├── init.lua
│ └── wrappers.lua
├── navi/
│ └── init.lua
├── notify/
│ └── init.lua
├── panel/
│ ├── autocmds.lua
│ └── init.lua
├── state/
│ ├── autocmds.lua
│ └── init.lua
├── term/
│ └── init.lua
├── tree/
│ ├── init.lua
│ ├── marshal/
│ │ └── init.lua
│ └── node.lua
└── util/
├── buffer.lua
├── init.lua
├── path.lua
└── window.lua
Condensed preview — 30 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (137K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 66,
"preview": "# These are supported funding model platforms\n\ngithub: [ldelossa]\n"
},
{
"path": ".gitignore",
"chars": 9,
"preview": "notes.md\n"
},
{
"path": "README.md",
"chars": 2485,
"preview": "```\n██╗ ██╗████████╗███████╗███████╗ ███╗ ██╗██╗ ██╗██╗███╗ ███╗\n██║ ██║╚══██╔══╝██╔════╝██╔════╝ ████"
},
{
"path": "doc/litee.txt",
"chars": 22488,
"preview": "*litee.nvim* litee.nvim\n\nAuthor: Louis DeLosSantos <louis.delos@gmail.com>\nHomepage: <https://github.com/ldelossa/lite"
},
{
"path": "doc/tags",
"chars": 748,
"preview": "after\tlitee.txt\t/*after*\nbefore\tlitee.txt\t/*before*\nlitee-contents\tlitee.txt\t/*litee-contents*\nlitee-intro\tlitee.txt\t/*l"
},
{
"path": "lua/litee/lib/commands.lua",
"chars": 568,
"preview": "local M = {}\n\n-- commands will setup any Vim commands exported on litee.lib\n-- setup.\nfunction M.setup()\n vim.cmd(\"co"
},
{
"path": "lua/litee/lib/config.lua",
"chars": 617,
"preview": "local M = {}\n\n-- config is a global configuration object\n-- for each module in the litee library.\n--\n-- config for a par"
},
{
"path": "lua/litee/lib/details/init.lua",
"chars": 2023,
"preview": "local M = {}\n\n-- a singleton float window for a details popup.\nlocal float_win = nil\n\n-- close_details_popups closes the"
},
{
"path": "lua/litee/lib/highlights/auto.lua",
"chars": 1869,
"preview": "local lib_util = require('litee.lib.util')\nlocal lib_hi = require('litee.lib.highlights')\n\nlocal M = {}\n\n-- the curr"
},
{
"path": "lua/litee/lib/highlights/init.lua",
"chars": 5463,
"preview": "local M = {}\n\n-- hls is a map of UI specific highlights used\n-- by the litee.nvim library.\nM.hls = {\n SymbolDetailHL "
},
{
"path": "lua/litee/lib/icons/init.lua",
"chars": 9566,
"preview": "local M = {}\n\n-- a lot of these are yoinked from:\n-- https://github.com/onsails/lspkind-nvim/blob/master/lua/lspkind/ini"
},
{
"path": "lua/litee/lib/init.lua",
"chars": 2530,
"preview": "local commands = require('litee.lib.commands')\nlocal config = require('litee.lib.config').config\nlocal lib_hi = require("
},
{
"path": "lua/litee/lib/jumps/init.lua",
"chars": 7640,
"preview": "local config = require('litee.lib.config').config\nlocal lib_hi = require('litee.lib.highlights')\nlocal lib_panel ="
},
{
"path": "lua/litee/lib/lsp/hover.lua",
"chars": 2171,
"preview": "local M = {}\n\nlocal float_win = nil\n\n-- close_hover_popups closes the created popup window\n-- if it exists.\nfunction M.c"
},
{
"path": "lua/litee/lib/lsp/init.lua",
"chars": 5515,
"preview": "local lib_notify = require('litee.lib.notify')\nlocal lib_tree_node = require('litee.lib.tree.node')\n\nlocal M = {}\n\n-- mu"
},
{
"path": "lua/litee/lib/lsp/wrappers.lua",
"chars": 555,
"preview": "local lib_notify = require('litee.lib.notify')\nlocal M = {}\n\nfunction M.buf_document_symbol()\n lib_notify.notify_popu"
},
{
"path": "lua/litee/lib/navi/init.lua",
"chars": 1740,
"preview": "local M = {}\n\n-- next moves the cursor in the window of the provided\n-- component_state forward\n--\n-- @param component_s"
},
{
"path": "lua/litee/lib/notify/init.lua",
"chars": 2218,
"preview": "local notify_config = require('litee.lib.config').config[\"notify\"]\nlocal M = {}\n\nlocal float_wins = {}\n\nfunction M.close"
},
{
"path": "lua/litee/lib/panel/autocmds.lua",
"chars": 519,
"preview": "local lib_state = require('litee.lib.state')\nlocal lib_panel = require('litee.lib.panel')\n\nlocal M = {}\n\nfunction M.on_r"
},
{
"path": "lua/litee/lib/panel/init.lua",
"chars": 18300,
"preview": "local lib_state = require('litee.lib.state')\nlocal config = require('litee.lib.config').config\nlocal lib_notify = requir"
},
{
"path": "lua/litee/lib/state/autocmds.lua",
"chars": 393,
"preview": "local lib_tree = require('litee.lib.tree')\nlocal lib_state = require('litee.lib.state')\n\nlocal M = {}\n\nM.on_tab_closed "
},
{
"path": "lua/litee/lib/state/init.lua",
"chars": 3348,
"preview": "local M = {}\n\n-- registry is a per-tab-page registry for\n-- state.\n--\n-- state defines information necessary for\n-- a UI"
},
{
"path": "lua/litee/lib/term/init.lua",
"chars": 4769,
"preview": "local lib_panel = require('litee.lib.panel')\nlocal lib_state = require('litee.lib.state')\nlocal lib_util_buf = require('"
},
{
"path": "lua/litee/lib/tree/init.lua",
"chars": 10784,
"preview": "local lib_marshal = require('litee.lib.tree.marshal')\n\nlocal M = {}\n\n-- registry keeps a mapping between tree handles\n--"
},
{
"path": "lua/litee/lib/tree/marshal/init.lua",
"chars": 6379,
"preview": "local lib_util = require('litee.lib.util')\nlocal lib_hi = require('litee.lib.highlights')\nlocal lib_tree_config = req"
},
{
"path": "lua/litee/lib/tree/node.lua",
"chars": 2010,
"preview": "local M = {}\n\n-- new_node creates a polymorphic node\n-- which the caller can attach their own\n-- node specific fields to"
},
{
"path": "lua/litee/lib/util/buffer.lua",
"chars": 3956,
"preview": "local M = {}\n\n\nlocal original_guicursor = \"\"\n-- hide_cursor will dynamically create the LTCursorHide hi and\n-- set the g"
},
{
"path": "lua/litee/lib/util/init.lua",
"chars": 3352,
"preview": "local M = {}\n\nfunction M.absolute_path_from_uri(uri)\n local uri_path = vim.fn.substitute(uri, \"file://\", \"\", \"\")\n "
},
{
"path": "lua/litee/lib/util/path.lua",
"chars": 3169,
"preview": "local M = {}\n\n-- safe_encode returns a string which is safe to\n-- use as a filesystem name.\n--\n-- this is helpful when e"
},
{
"path": "lua/litee/lib/util/window.lua",
"chars": 2902,
"preview": "local lib_state = require('litee.lib.state')\nlocal lib_tree_config = require('litee.lib.config').config[\"tree\""
}
]
About this extraction
This page contains the full source code of the ldelossa/litee.nvim GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 30 files (125.1 KB), approximately 32.2k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.