@@ -9,6 +9,7 @@ local Hyperlink = require('orgmode.org.links.hyperlink')
99local Range = require (' orgmode.files.elements.range' )
1010local Footnote = require (' orgmode.objects.footnote' )
1111local Memoize = require (' orgmode.utils.memoize' )
12+ local is_nightly = vim .fn .has (' nvim-0.12' ) > 0
1213
1314--- @class OrgFileMetadata
1415--- @field mtime number File modified time in nanoseconds
@@ -17,13 +18,15 @@ local Memoize = require('orgmode.utils.memoize')
1718
1819--- @class OrgFileOpts
1920--- @field filename string
21+ --- @field lines ? string[]
2022--- @field buf ? number
2123
2224--- @class OrgFile
2325--- @field filename string
2426--- @field buf number
2527--- @field index number
2628--- @field lines string[]
29+ --- @field content string
2730--- @field metadata OrgFileMetadata
2831--- @field parser vim.treesitter.LanguageTree
2932--- @field root TSNode
@@ -45,18 +48,19 @@ function OrgFile:new(opts)
4548 filename = opts .filename ,
4649 index = 0 ,
4750 buf = opts .buf or - 1 ,
48- lines = {},
51+ lines = opts .lines or {},
52+ content = table.concat (opts .lines or {}, ' \n ' ),
4953 metadata = {
5054 mtime = stat and stat .mtime .nsec or 0 ,
5155 mtime_sec = stat and stat .mtime .sec or 0 ,
5256 changedtick = opts .buf and vim .api .nvim_buf_get_changedtick (opts .buf ) or 0 ,
5357 },
5458 }
55- if data .buf > 0 then
56- data .lines = self :_get_lines (data .buf )
59+ local this = setmetatable (data , self )
60+ if this .buf > 0 then
61+ this :_update_lines (this :_get_lines (this .buf ))
5762 end
58- setmetatable (data , self )
59- return data
63+ return this
6064end
6165
6266--- Load the file
@@ -75,12 +79,23 @@ function OrgFile.load(filename)
7579 return Promise .resolve (false )
7680 end
7781
78- bufnr = OrgFile ._load_buffer (filename )
82+ -- TODO: Remove once Neovim adds string parser back
83+ -- See: https://github.com/nvim-orgmode/orgmode/issues/1049
84+ if is_nightly then
85+ bufnr = OrgFile ._load_buffer (filename )
7986
80- return Promise .resolve (OrgFile :new ({
81- filename = filename ,
82- buf = bufnr ,
83- }))
87+ return Promise .resolve (OrgFile :new ({
88+ filename = filename ,
89+ buf = bufnr ,
90+ }))
91+ end
92+
93+ return utils .readfile (filename , { schedule = true }):next (function (lines )
94+ return OrgFile :new ({
95+ filename = filename ,
96+ lines = lines ,
97+ })
98+ end )
8499end
85100
86101--- Reload the file if it has been modified
@@ -90,16 +105,17 @@ function OrgFile:reload()
90105 return Promise .resolve (self )
91106 end
92107
108+
93109 local bufnr = self :bufnr ()
94110 local buf_changed = false
95111 local file_changed = false
96112
97- if bufnr then
113+ if bufnr > - 1 then
98114 local new_changedtick = vim .api .nvim_buf_get_changedtick (bufnr )
99115 buf_changed = self .metadata .changedtick ~= new_changedtick
100116 self .metadata .changedtick = new_changedtick
101117 if buf_changed then
102- self . lines = self :_get_lines (bufnr )
118+ self : _update_lines ( self :_get_lines (bufnr ) )
103119 end
104120 end
105121 local stat = vim .uv .fs_stat (self .filename )
@@ -114,7 +130,7 @@ function OrgFile:reload()
114130
115131 if file_changed and not buf_changed then
116132 return utils .readfile (self .filename , { schedule = true }):next (function (lines )
117- self . lines = lines
133+ self : _update_lines ( lines )
118134 return self
119135 end )
120136 end
@@ -184,7 +200,7 @@ function OrgFile:parse(skip_if_not_modified)
184200 if skip_if_not_modified and self .root and not self :is_modified () then
185201 return self .root
186202 end
187- self .parser = ts . get_parser ( self :bufnr (), ' org ' , {} )
203+ self .parser = self :_get_parser ( )
188204 local trees = self .parser :parse ()
189205 self .root = trees [1 ]:root ()
190206 return self .root
@@ -203,7 +219,7 @@ function OrgFile:get_ts_matches(query, parent_node)
203219 local ts_query = ts_utils .get_query (query )
204220 local matches = {}
205221
206- for _ , match , _ in ts_query :iter_matches (parent_node , self :bufnr (), nil , nil , { all = true }) do
222+ for _ , match , _ in ts_query :iter_matches (parent_node , self :get_source (), nil , nil , { all = true }) do
207223 local items = {}
208224 for id , nodes in pairs (match ) do
209225 local name = ts_query .captures [id ]
@@ -233,7 +249,7 @@ function OrgFile:get_ts_captures(query, node)
233249 local ts_query = ts_utils .get_query (query )
234250 local matches = {}
235251
236- for _ , match in ts_query :iter_captures (node , self :bufnr ()) do
252+ for _ , match in ts_query :iter_captures (node , self :get_source ()) do
237253 table.insert (matches , match )
238254 end
239255 return matches
@@ -489,13 +505,13 @@ function OrgFile:get_node_text(node, range)
489505 return ' '
490506 end
491507 if range then
492- return ts .get_node_text (node , self :bufnr (), {
508+ return ts .get_node_text (node , self :get_source (), {
493509 metadata = {
494510 range = range ,
495511 },
496512 })
497513 end
498- return ts .get_node_text (node , self :bufnr ())
514+ return ts .get_node_text (node , self :get_source ())
499515end
500516
501517--- @param node ? TSNode
@@ -557,15 +573,27 @@ end
557573
558574--- @return number
559575function OrgFile :bufnr ()
560- local bufnr = self .buf
576+ -- TODO: Remove once Neovim adds string parser back
577+ -- See: https://github.com/nvim-orgmode/orgmode/issues/1049
578+ if is_nightly then
579+ local bufnr = self .buf
580+ -- Do not consider unloaded buffers as valid
581+ -- Treesitter is not working in them
582+ if bufnr > - 1 and vim .api .nvim_buf_is_loaded (bufnr ) then
583+ return bufnr
584+ end
585+ local new_bufnr = self ._load_buffer (self .filename )
586+ self .buf = new_bufnr
587+ return new_bufnr
588+ end
589+
590+ local bufnr = utils .get_buffer_by_filename (self .filename )
561591 -- Do not consider unloaded buffers as valid
562592 -- Treesitter is not working in them
563593 if bufnr > - 1 and vim .api .nvim_buf_is_loaded (bufnr ) then
564594 return bufnr
565595 end
566- local new_bufnr = self ._load_buffer (self .filename )
567- self .buf = new_bufnr
568- return new_bufnr
596+ return - 1
569597end
570598
571599--- @private
@@ -819,7 +847,7 @@ function OrgFile:get_links()
819847 (link_desc) @link
820848 ]] )
821849
822- local source = self :bufnr ()
850+ local source = self :get_source ()
823851 for _ , node in ipairs (matches ) do
824852 table.insert (links , Hyperlink .from_node (node , source ))
825853 end
@@ -840,7 +868,7 @@ function OrgFile:get_footnote_references()
840868
841869 local footnotes = {}
842870 local processed_lines = {}
843- for _ , match in ts_query :iter_captures (self .root , self :bufnr ()) do
871+ for _ , match in ts_query :iter_captures (self .root , self :get_source ()) do
844872 local line_start , _ , line_end = match :range ()
845873 if not processed_lines [line_start ] then
846874 if line_start == line_end then
@@ -947,6 +975,13 @@ function OrgFile:_get_directive(directive_name, all_matches)
947975 return nil
948976end
949977
978+ function OrgFile :_update_lines (lines )
979+ self .lines = lines
980+ self .content = table.concat (lines , ' \n ' )
981+ self :parse ()
982+ return self
983+ end
984+
950985--- @private
951986--- Get all buffer lines, ensure empty buffer returns empty table
952987--- @return string[]
@@ -958,4 +993,34 @@ function OrgFile:_get_lines(bufnr)
958993 return lines
959994end
960995
996+ --- @private
997+ --- @return vim.treesitter.LanguageTree
998+ function OrgFile :_get_parser ()
999+ local bufnr = self :bufnr ()
1000+
1001+ if bufnr > - 1 then
1002+ -- Always get the fresh parser for the buffer
1003+ return ts .get_parser (bufnr , ' org' , {})
1004+ end
1005+
1006+ -- In case the buffer got unloaded, go back to string parser
1007+ if not self .parser or self :is_modified () or type (self .parser :source ()) == ' number' then
1008+ return ts .get_string_parser (self .content , ' org' , {})
1009+ end
1010+
1011+ return self .parser
1012+ end
1013+
1014+ --- Get the ts source for the file
1015+ --- If there is a buffer, return buffer number
1016+ --- Otherwise, return the string content
1017+ --- @return integer | string
1018+ function OrgFile :get_source ()
1019+ local bufnr = self :bufnr ()
1020+ if bufnr > - 1 then
1021+ return bufnr
1022+ end
1023+ return self .content
1024+ end
1025+
9611026return OrgFile
0 commit comments