@@ -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
@@ -94,12 +109,12 @@ function OrgFile:reload()
94109 local buf_changed = false
95110 local file_changed = false
96111
97- if bufnr then
112+ if bufnr > - 1 then
98113 local new_changedtick = vim .api .nvim_buf_get_changedtick (bufnr )
99114 buf_changed = self .metadata .changedtick ~= new_changedtick
100- self .metadata .changedtick = new_changedtick
101115 if buf_changed then
102- self .lines = self :_get_lines (bufnr )
116+ self :_update_lines (self :_get_lines (bufnr ))
117+ self .metadata .changedtick = new_changedtick
103118 end
104119 end
105120 local stat = vim .uv .fs_stat (self .filename )
@@ -108,13 +123,15 @@ function OrgFile:reload()
108123 local new_mtime_sec = stat .mtime .sec
109124 file_changed = (new_mtime_nsec > 0 and self .metadata .mtime ~= new_mtime_nsec )
110125 or self .metadata .mtime_sec ~= new_mtime_sec
111- self .metadata .mtime = new_mtime_nsec
112- self .metadata .mtime_sec = new_mtime_sec
113126 end
114127
115128 if file_changed and not buf_changed then
116129 return utils .readfile (self .filename , { schedule = true }):next (function (lines )
117- self .lines = lines
130+ self :_update_lines (lines )
131+ if stat then
132+ self .metadata .mtime = stat .mtime .nsec
133+ self .metadata .mtime_sec = stat .mtime .sec
134+ end
118135 return self
119136 end )
120137 end
@@ -184,7 +201,7 @@ function OrgFile:parse(skip_if_not_modified)
184201 if skip_if_not_modified and self .root and not self :is_modified () then
185202 return self .root
186203 end
187- self .parser = ts . get_parser ( self :bufnr (), ' org ' , {} )
204+ self .parser = self :_get_parser ( )
188205 local trees = self .parser :parse ()
189206 self .root = trees [1 ]:root ()
190207 return self .root
@@ -203,7 +220,7 @@ function OrgFile:get_ts_matches(query, parent_node)
203220 local ts_query = ts_utils .get_query (query )
204221 local matches = {}
205222
206- for _ , match , _ in ts_query :iter_matches (parent_node , self :bufnr (), nil , nil , { all = true }) do
223+ for _ , match , _ in ts_query :iter_matches (parent_node , self :get_source (), nil , nil , { all = true }) do
207224 local items = {}
208225 for id , nodes in pairs (match ) do
209226 local name = ts_query .captures [id ]
@@ -233,7 +250,7 @@ function OrgFile:get_ts_captures(query, node)
233250 local ts_query = ts_utils .get_query (query )
234251 local matches = {}
235252
236- for _ , match in ts_query :iter_captures (node , self :bufnr ()) do
253+ for _ , match in ts_query :iter_captures (node , self :get_source ()) do
237254 table.insert (matches , match )
238255 end
239256 return matches
@@ -489,13 +506,13 @@ function OrgFile:get_node_text(node, range)
489506 return ' '
490507 end
491508 if range then
492- return ts .get_node_text (node , self :bufnr (), {
509+ return ts .get_node_text (node , self :get_source (), {
493510 metadata = {
494511 range = range ,
495512 },
496513 })
497514 end
498- return ts .get_node_text (node , self :bufnr ())
515+ return ts .get_node_text (node , self :get_source ())
499516end
500517
501518--- @param node ? TSNode
@@ -557,15 +574,27 @@ end
557574
558575--- @return number
559576function OrgFile :bufnr ()
560- local bufnr = self .buf
577+ -- TODO: Remove once Neovim adds string parser back
578+ -- See: https://github.com/nvim-orgmode/orgmode/issues/1049
579+ if is_nightly then
580+ local bufnr = self .buf
581+ -- Do not consider unloaded buffers as valid
582+ -- Treesitter is not working in them
583+ if bufnr > - 1 and vim .api .nvim_buf_is_loaded (bufnr ) then
584+ return bufnr
585+ end
586+ local new_bufnr = self ._load_buffer (self .filename )
587+ self .buf = new_bufnr
588+ return new_bufnr
589+ end
590+
591+ local bufnr = utils .get_buffer_by_filename (self .filename )
561592 -- Do not consider unloaded buffers as valid
562593 -- Treesitter is not working in them
563594 if bufnr > - 1 and vim .api .nvim_buf_is_loaded (bufnr ) then
564595 return bufnr
565596 end
566- local new_bufnr = self ._load_buffer (self .filename )
567- self .buf = new_bufnr
568- return new_bufnr
597+ return - 1
569598end
570599
571600--- @private
@@ -819,7 +848,7 @@ function OrgFile:get_links()
819848 (link_desc) @link
820849 ]] )
821850
822- local source = self :bufnr ()
851+ local source = self :get_source ()
823852 for _ , node in ipairs (matches ) do
824853 table.insert (links , Hyperlink .from_node (node , source ))
825854 end
@@ -840,7 +869,7 @@ function OrgFile:get_footnote_references()
840869
841870 local footnotes = {}
842871 local processed_lines = {}
843- for _ , match in ts_query :iter_captures (self .root , self :bufnr ()) do
872+ for _ , match in ts_query :iter_captures (self .root , self :get_source ()) do
844873 local line_start , _ , line_end = match :range ()
845874 if not processed_lines [line_start ] then
846875 if line_start == line_end then
@@ -947,6 +976,13 @@ function OrgFile:_get_directive(directive_name, all_matches)
947976 return nil
948977end
949978
979+ function OrgFile :_update_lines (lines )
980+ self .lines = lines
981+ self .content = table.concat (lines , ' \n ' )
982+ self :parse ()
983+ return self
984+ end
985+
950986--- @private
951987--- Get all buffer lines, ensure empty buffer returns empty table
952988--- @return string[]
@@ -958,4 +994,34 @@ function OrgFile:_get_lines(bufnr)
958994 return lines
959995end
960996
997+ --- @private
998+ --- @return vim.treesitter.LanguageTree
999+ function OrgFile :_get_parser ()
1000+ local bufnr = self :bufnr ()
1001+
1002+ if bufnr > - 1 then
1003+ -- Always get the fresh parser for the buffer
1004+ return ts .get_parser (bufnr , ' org' , {})
1005+ end
1006+
1007+ -- In case the buffer got unloaded, go back to string parser
1008+ if not self .parser or self :is_modified () or type (self .parser :source ()) == ' number' then
1009+ return ts .get_string_parser (self .content , ' org' , {})
1010+ end
1011+
1012+ return self .parser
1013+ end
1014+
1015+ --- Get the ts source for the file
1016+ --- If there is a buffer, return buffer number
1017+ --- Otherwise, return the string content
1018+ --- @return integer | string
1019+ function OrgFile :get_source ()
1020+ local bufnr = self :bufnr ()
1021+ if bufnr > - 1 then
1022+ return bufnr
1023+ end
1024+ return self .content
1025+ end
1026+
9611027return OrgFile
0 commit comments