Skip to content

Commit

Permalink
Add option to format tables with gq mapping (formatexpr)
Browse files Browse the repository at this point in the history
  • Loading branch information
kristijanhusak committed May 29, 2022
1 parent 62a7873 commit 87291af
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 21 deletions.
38 changes: 31 additions & 7 deletions DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@
6. [Text objects](#text-objects)
7. [Dot repeat](#dot-repeat)
4. [Document Diagnostics](#document-diagnostics)
5. [Autocompletion](#autocompletion)
6. [Abbreviations](#abbreviations)
7. [Colors](#colors)
8. [Advanced search](#advanced-search)
9. [Notifications (experimental)](#notifications-experimental)
10. [Clocking](#clocking)
11. [Changelog](#changelog)
5. [Tables](#tables)
6. [Autocompletion](#autocompletion)
7. [Abbreviations](#abbreviations)
8. [Colors](#colors)
9. [Advanced search](#advanced-search)
10. [Notifications (experimental)](#notifications-experimental)
11. [Clocking](#clocking)
12. [Changelog](#changelog)

## Getting started with Orgmode
To get a basic idea how Orgmode works, look at this screencast from [@dhruvasagar](https://github.com/dhruvasagar)
Expand Down Expand Up @@ -945,6 +946,29 @@ To make all mappings dot repeatable, install [vim-repeat](https://github.com/tpo
Since tree-sitter parser is being used to parse the file, if there are some syntax errors,
it can potentially fail to parse specific parts of document when needed.

## Tables
Tables can be formatted via built in `formatexpr` (see `:help gq`)

For example, having this content:
```
* TODO My headline
DEADLINE: <2022-05-22 Sun>
|Header 1|Header 2
|-
| col 1| col 2|
```

And going to line `4` and pressing `gqgq`, it will format it to this:
```
* TODO My headline
DEADLINE: <2022-05-22 Sun>
| Header 1 | Header 2 |
|----------+----------|
| col 1 | col 2 |
```

## Autocompletion
By default, `omnifunc` is provided in `org` files that autocompletes these types:
* Tags
Expand Down
4 changes: 2 additions & 2 deletions lua/orgmode/clock/report.lua
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ end
function ClockReport:draw_for_agenda(start_line)
local data = {
{ 'File', 'Headline', 'Time' },
{},
'hr',
{ '', 'ALL Total time', self.total_duration:to_string() },
{},
'hr',
}

for _, file in ipairs(self.files) do
Expand Down
17 changes: 13 additions & 4 deletions lua/orgmode/org/format.lua
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
local Files = require('orgmode.parser.files')
local Table = require('orgmode.treesitter.table')

local function format()
local line = vim.fn.line('.')
local ok, item = pcall(Files.get_closest_headline, line)
if not ok or not item then
if vim.tbl_contains({ 'i', 'R', 'ic', 'ix' }, vim.fn.mode()) then
-- `formatexpr` is also called when exceeding `textwidth` in insert mode
-- fall back to internal formatting
return 1
end

if item.logbook and item.logbook.range:is_in_line_range(line) then
local table = Table.format()

if table then
return 0
end

local line = vim.fn.line('.')
local ok, item = pcall(Files.get_closest_headline, line)
if ok and item and item.logbook and item.logbook.range:is_in_line_range(line) then
return item.logbook:recalculate_estimate(line)
end

Expand Down
2 changes: 0 additions & 2 deletions lua/orgmode/org/mappings.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ local config = require('orgmode.config')
local constants = require('orgmode.utils.constants')
local ts_utils = require('nvim-treesitter.ts_utils')
local utils = require('orgmode.utils')
local tree_utils = require('orgmode.utils.treesitter')
local ts_org = require('orgmode.treesitter')
local Listitem = require('orgmode.treesitter.listitem')

---@class OrgMappings
---@field capture Capture
Expand Down
12 changes: 8 additions & 4 deletions lua/orgmode/parser/table/row.lua
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,22 @@ function TableRow:to_string()
return self.content
end

---@param row table
---@param row table | string
---@param line number
---@param parent_table Table
---@return TableRow
function TableRow.from_table_item(row, line, parent_table)
local table_row = TableRow:new({
table = parent_table,
line = line,
is_separator = #row == 0,
is_separator = type(row) == 'string' and row == 'hr',
})
for col_nr, cell_data in ipairs(row) do
table_row:add_cell(TableCell.from_row_item(cell_data, col_nr, table_row))
if type(row) == 'string' then
table_row:add_cell(TableCell.from_row_item('', 1, table_row))
else
for col_nr, cell_data in ipairs(row) do
table_row:add_cell(TableCell.from_row_item(cell_data, col_nr, table_row))
end
end
return table_row
end
Expand Down
88 changes: 88 additions & 0 deletions lua/orgmode/treesitter/table.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
local ts_utils = require('nvim-treesitter.ts_utils')
local Table = require('orgmode.parser.table')
local utils = require('orgmode.utils')
local config = require('orgmode.config')
local query = vim.treesitter.query

---@class TsTable
---@field node userdata
---@field data table[]
local TsTable = {}

function TsTable:new(opts)
local data = {}
data.node = opts.node
setmetatable(data, self)
self.__index = self
data:_parse_data()
return data
end

---@private
--- Parse table data from node
function TsTable:_parse_data()
local rows = {}
for row in self.node:iter_children() do
if row:type() == 'hr' then
table.insert(rows, 'hr')
end
if row:type() == 'row' then
local row_data = {}
for cell in row:iter_children() do
if cell:type() == 'cell' then
local cell_val = ''
local cell_content = cell:field('contents')
if cell_content and #cell_content > 0 then
cell_val = query.get_node_text(cell_content[1], 0)
end
table.insert(row_data, cell_val)
end
end
table.insert(rows, row_data)
end
end

self.data = rows
end

function TsTable:rerender()
local start_row, start_col = self.node:range()
local tbl = Table.from_list(self.data, start_row + 1, start_col + 1)
local first_line = vim.api.nvim_buf_get_lines(0, start_row, start_row + 1, true)
local indent = first_line and first_line[1]:match('^%s*') or ''
indent = config:get_indent(indent:len())

local contents = tbl:draw()
local indented = {}
for _, content in ipairs(contents) do
table.insert(indented, string.format('%s%s', indent, content))
end
local view = vim.fn.winsaveview()
vim.api.nvim_buf_set_lines(0, tbl.range.start_line - 1, tbl.range.end_line - 1, false, indented)
vim.fn.winrestview(view)
end

local function format()
local view = vim.fn.winsaveview()
-- Go to first non blank char
vim.cmd([[norm! _]])
local node = ts_utils.get_node_at_cursor()
vim.fn.winrestview(view)

if not node then
return false
end
print(node:type())
print(node:type())
print(node:type())
local table_node = utils.get_closest_parent_of_type(node, 'table', true)
if not table_node then
return false
end
TsTable:new({ node = table_node }):rerender()
return true
end

return {
format = format,
}
4 changes: 2 additions & 2 deletions tests/plenary/parser/table_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ describe('Table', function()
local data = {
{ 'one', 'two', 'three' },
{ 'four', 'five', 'six', 'seven' },
{},
'hr',
{ 'eight' },
{ 'nine', 'ten' },
}
Expand Down Expand Up @@ -37,7 +37,7 @@ describe('Table', function()

local data_with_long_names = {
{ 'one', 'two', 'three' },
{},
'hr',
{ 'four', 'five', 'six', 'seven longer long' },
{ 'eight', 'iamverylong' },
{ 'nine', 'ten', 'a' },
Expand Down
56 changes: 56 additions & 0 deletions tests/plenary/ui/table_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
local helpers = require('tests.plenary.ui.helpers')

describe('Tables', function()
after_each(function()
vim.cmd([[silent! %bw!]])
end)

it('should generate basic table structure from pipe or hr line', function()
helpers.load_file_content({
'|',
})
vim.fn.cursor({ 1, 1 })
vim.cmd([[norm gqgq]])
assert.are.same({ '| |' }, vim.api.nvim_buf_get_lines(0, 0, -1, false))

helpers.load_file_content({
'|-',
})

vim.cmd([[norm gqgq]])
assert.are.same({ '|--|' }, vim.api.nvim_buf_get_lines(0, 0, -1, false))
end)

it('should format the table', function()
helpers.load_file_content({
' |head1 | ',
' |- ',
' |content| ',
})
vim.fn.cursor({ 2, 1 })
vim.cmd([[norm! gqgq]])

assert.are.same({
' | head1 |',
' |---------|',
' | content |',
}, vim.api.nvim_buf_get_lines(0, 0, -1, false))
end)

it('should format multi column table', function()
helpers.load_file_content({
'|first|second|',
'|-',
'|third cell| fourth cell|',
'|fifth|sixth| seventh',
})
vim.fn.cursor({ 1, 1 })
vim.cmd([[norm gqgq]])
assert.are.same({
'| first | second | |',
'|------------+-------------+---------|',
'| third cell | fourth cell | |',
'| fifth | sixth | seventh |',
}, vim.api.nvim_buf_get_lines(0, 0, -1, false))
end)
end)

0 comments on commit 87291af

Please sign in to comment.