@@ -24,6 +24,10 @@ It takes 3 arguments:
2424- `filetype` - the filetype to export to
2525- `output-dir` (optional) - a custom output directory to use. If not provided will fall back to `config.public.export_dir`
2626 (see [configuration](#configuration)).
27+
28+ And if you just want to export a snippet from a single document, you can use `:Neorg export
29+ to-clipboard <filetype>`. Filetype is required, at the time of writing the only option is "markdown".
30+ This copies the range given to the command to the `+` register.
2731--]]
2832
2933local neorg = require (" neorg.core" )
@@ -39,7 +43,12 @@ module.setup = function()
3943 }
4044end
4145
46+ --- @type core.integrations.treesitter
47+ local ts
48+
4249module .load = function ()
50+ ts = module .required [" core.integrations.treesitter" ]
51+
4352 modules .await (" core.neorgcmd" , function (neorgcmd )
4453 neorgcmd .add_commands_from_table ({
4554 export = {
@@ -57,6 +66,10 @@ module.load = function()
5766 max_args = 3 ,
5867 name = " export.directory" ,
5968 },
69+ [" to-clipboard" ] = {
70+ args = 1 ,
71+ name = " export.to-clipboard" ,
72+ },
6073 },
6174 },
6275 })
@@ -84,39 +97,56 @@ module.public = {
8497 return modules .get_module (" core.export." .. ftype ), modules .get_module_config (" core.export." .. ftype )
8598 end ,
8699
100+ --- export part of a buffer
101+ --- @param buffer number
102+ --- @param start_row number 1 indexed
103+ --- @param end_row number 1 indexed , inclusive
104+ --- @param filetype string
105+ --- @return string ? content , string ? extension exported content as a string , and the extension
106+ --- used for the export
107+ export_range = function (buffer , start_row , end_row , filetype )
108+ local converter = module .private .get_converter_checked (filetype )
109+ if not converter then return end
110+ local content = vim .iter (vim .api .nvim_buf_get_lines (buffer , start_row - 1 , end_row , false )):join (" \n " )
111+ local root = ts .get_document_root (content )
112+ if not root then
113+ return
114+ end
115+
116+ return module .public .export_from_root (root , converter , content )
117+ end ,
118+
87119 --- Takes a buffer and exports it to a specific file
88120 --- @param buffer number #The buffer ID to read the contents from
89121 --- @param filetype string #A Neovim filetype to specify which language to export to
90122 --- @return string ?, string ? #The entire buffer parsed, converted and returned as a string, as well as the extension used for the export.
91123 export = function (buffer , filetype )
92- local converter , converter_config = module .public .get_converter (filetype )
93-
124+ local converter , converter_config = module .private .get_converter_checked (filetype )
94125 if not converter or not converter_config then
95- log .error (" Unable to export file - did not find exporter for filetype '" .. filetype .. " '." )
96126 return
97127 end
98128
99- -- Each converter must have a `extension` field in its public config
100- -- This is done to do a backwards lookup, e.g. `markdown` uses the `.md` file extension.
101- if not converter_config .extension then
102- log .error (
103- " Unable to export file - exporter for filetype '"
104- .. filetype
105- .. " ' did not return a preferred extension. The exporter is unable to infer extensions."
106- )
107- return
108- end
109-
110- local document_root = module .required [" core.integrations.treesitter" ].get_document_root (buffer )
129+ local document_root = ts .get_document_root (buffer )
111130
112131 if not document_root then
113132 return
114133 end
115134
135+ local content = module .public .export_from_root (document_root , converter , buffer )
136+
137+ return content , converter_config .extension
138+ end ,
139+
140+ --- Do the work of exporting the given TS node via the given converter
141+ --- @param root TSNode
142+ --- @param converter table
143+ --- @param source number | string
144+ --- @return string
145+ export_from_root = function (root , converter , source )
116146 -- Initialize the state. The state is a table that exists throughout the entire duration
117147 -- of the export, and can be used to e.g. retain indent levels and/or keep references.
118148 local state = converter .export .init_state and converter .export .init_state () or {}
119- local ts_utils = module . required [ " core.integrations.treesitter " ] .get_ts_utils ()
149+ local ts_utils = ts .get_ts_utils ()
120150
121151 --- Descends down a node and its children
122152 --- @param start table #The TS node to begin at
@@ -144,7 +174,7 @@ module.public = {
144174 -- `keep_descending` - if true will continue to recurse down the current node's children despite the current
145175 -- node already being parsed
146176 -- `state` - a modified version of the state that then gets merged into the main state table
147- local result = exporter (vim .treesitter .get_node_text (node , buffer ), node , state , ts_utils )
177+ local result = exporter (vim .treesitter .get_node_text (node , source ), node , state , ts_utils )
148178
149179 if type (result ) == " table" then
150180 state = result .state and vim .tbl_extend (" force" , state , result .state ) or state
@@ -155,8 +185,8 @@ module.public = {
155185
156186 if result .keep_descending then
157187 if state .parse_as then
158- node = module . required [ " core.integrations.treesitter " ] .get_document_root (
159- " \n " .. vim .treesitter .get_node_text (node , buffer ),
188+ node = ts .get_document_root (
189+ " \n " .. vim .treesitter .get_node_text (node , source ),
160190 state .parse_as
161191 )
162192 state .parse_as = nil
@@ -174,7 +204,7 @@ module.public = {
174204 elseif exporter == true then
175205 table.insert (
176206 output ,
177- module . required [ " core.integrations.treesitter " ]. get_node_text (node , buffer )
207+ ts . get_node_text (node , source )
178208 )
179209 else
180210 table.insert (output , exporter )
@@ -208,13 +238,42 @@ module.public = {
208238 or (not vim .tbl_isempty (output ) and table.concat (output ))
209239 end
210240
211- local output = descend (document_root )
241+ local output = descend (root )
212242
213243 -- Every converter can also come with a `cleanup` function that performs some final tweaks to the output string
214- return converter .export .cleanup and converter .export .cleanup (output ) or output , converter_config . extension
244+ return converter .export .cleanup and converter .export .cleanup (output ) or output
215245 end ,
216246}
217247
248+ module .private = {
249+ --- get the converter for the given filetype
250+ --- @param filetype string
251+ --- @return table ?, table ?
252+ get_converter_checked = function (filetype )
253+ local converter , converter_config = module .public .get_converter (filetype )
254+
255+ if not converter or not converter_config then
256+ log .error (" Unable to export file - did not find exporter for filetype '" .. filetype .. " '." )
257+ return
258+ end
259+
260+ -- Each converter must have a `extension` field in its public config
261+ -- This is done to do a backwards lookup, e.g. `markdown` uses the `.md` file extension.
262+ if not converter_config .extension then
263+ log .error (
264+ " Unable to export file - exporter for filetype '"
265+ .. filetype
266+ .. " ' did not return a preferred extension. The exporter is unable to infer extensions."
267+ )
268+ return
269+ end
270+
271+ return converter , converter_config
272+ end ,
273+
274+ }
275+
276+ --- @param event neorg.event
218277module .on_event = function (event )
219278 if event .type == " core.neorgcmd.events.export.to-file" then
220279 -- Syntax: Neorg export to-file file.extension forced-filetype?
@@ -234,6 +293,15 @@ module.on_event = function(event)
234293
235294 vim .schedule (lib .wrap (utils .notify , " Successfully exported 1 file!" ))
236295 end )
296+ elseif event .type == " core.neorgcmd.events.export.to-clipboard" then
297+ -- Syntax: Neorg export to-clipboard filetype
298+ -- Example: Neorg export to-clipboard markdown
299+
300+ local filetype = event .content [1 ]
301+ local data = event .content .data
302+ local exported = module .public .export_range (event .buffer , data .line1 , data .line2 , filetype )
303+
304+ vim .fn .setreg (" +" , exported , " l" )
237305 elseif event .type == " core.neorgcmd.events.export.directory" then
238306 local path = event .content [3 ] and vim .fn .expand (event .content [3 ])
239307 or module .config .public .export_dir
328396module .events .subscribed = {
329397 [" core.neorgcmd" ] = {
330398 [" export.to-file" ] = true ,
399+ [" export.to-clipboard" ] = true ,
331400 [" export.directory" ] = true ,
332401 },
333402}
0 commit comments