@@ -17,14 +17,13 @@ local Memoize = require('orgmode.utils.memoize')
1717
1818--- @class OrgFileOpts
1919--- @field filename string
20- --- @field lines string[]
21- --- @field bufnr ? number
20+ --- @field buf number
2221
2322--- @class OrgFile
2423--- @field filename string
24+ --- @field buf number
2525--- @field index number
2626--- @field lines string[]
27- --- @field content string
2827--- @field metadata OrgFileMetadata
2928--- @field parser vim.treesitter.LanguageTree
3029--- @field root TSNode
@@ -44,15 +43,17 @@ function OrgFile:new(opts)
4443 local stat = vim .uv .fs_stat (opts .filename )
4544 local data = {
4645 filename = opts .filename ,
47- lines = opts .lines ,
48- content = table.concat (opts .lines , ' \n ' ),
4946 index = 0 ,
47+ buf = opts .buf or - 1 ,
5048 metadata = {
5149 mtime = stat and stat .mtime .nsec or 0 ,
5250 mtime_sec = stat and stat .mtime .sec or 0 ,
53- changedtick = opts .bufnr and vim .api .nvim_buf_get_changedtick (opts .bufnr ) or 0 ,
51+ changedtick = opts .buf and vim .api .nvim_buf_get_changedtick (opts .buf ) or 0 ,
5452 },
5553 }
54+ if data .buf > 0 then
55+ data .lines = self :_get_lines (data .buf )
56+ end
5657 setmetatable (data , self )
5758 return data
5859end
6263function OrgFile .load (filename )
6364 local bufnr = utils .get_buffer_by_filename (filename )
6465
65- if
66- bufnr > - 1
67- and vim .api .nvim_buf_is_loaded (bufnr )
68- and vim .api .nvim_get_option_value (' filetype' , { buf = bufnr }) == ' org'
69- then
66+ if bufnr > - 1 and vim .api .nvim_buf_is_loaded (bufnr ) and vim .bo [bufnr ].filetype == ' org' then
7067 return Promise .resolve (OrgFile :new ({
7168 filename = filename ,
72- lines = vim .api .nvim_buf_get_lines (bufnr , 0 , - 1 , false ),
73- bufnr = bufnr ,
69+ buf = bufnr ,
7470 }))
7571 end
7672
7773 if not vim .uv .fs_stat (filename ) or not utils .is_org_file (filename ) then
7874 return Promise .resolve (false )
7975 end
8076
81- return utils .readfile (filename , { schedule = true }):next (function (lines )
82- return OrgFile :new ({
83- filename = filename ,
84- lines = lines ,
85- })
86- end )
77+ bufnr = vim .fn .bufadd (filename )
78+
79+ if bufnr == 0 then
80+ return Promise .resolve (false )
81+ end
82+
83+ vim .fn .bufload (bufnr )
84+
85+ return Promise .resolve (OrgFile :new ({
86+ filename = filename ,
87+ buf = bufnr ,
88+ }))
8789end
8890
8991--- Reload the file if it has been modified
@@ -94,15 +96,34 @@ function OrgFile:reload()
9496 end
9597
9698 local bufnr = self :bufnr ()
99+ local buf_changed = false
100+ local file_changed = false
97101
98- if bufnr > - 1 then
99- local updated_file = self :_update_lines (vim .api .nvim_buf_get_lines (bufnr , 0 , - 1 , false ), bufnr )
100- return Promise .resolve (updated_file )
102+ if bufnr then
103+ local new_changedtick = vim .api .nvim_buf_get_changedtick (bufnr )
104+ buf_changed = self .metadata .changedtick ~= new_changedtick
105+ self .metadata .changedtick = new_changedtick
106+ if buf_changed then
107+ self .lines = self :_get_lines (bufnr )
108+ end
101109 end
102-
103- return utils .readfile (self .filename , { schedule = true }):next (function (lines )
104- return self :_update_lines (lines )
105- end )
110+ local stat = vim .uv .fs_stat (self .filename )
111+ if stat then
112+ local new_mtime_nsec = stat .mtime .nsec
113+ local new_mtime_sec = stat .mtime .sec
114+ file_changed = (new_mtime_nsec > 0 and self .metadata .mtime ~= new_mtime_nsec )
115+ or self .metadata .mtime_sec ~= new_mtime_sec
116+ self .metadata .mtime = new_mtime_nsec
117+ self .metadata .mtime_sec = new_mtime_sec
118+ end
119+
120+ if file_changed and not buf_changed then
121+ return utils .readfile (self .filename , { schedule = true }):next (function (lines )
122+ self .lines = lines
123+ return self
124+ end )
125+ end
126+ return Promise .resolve (self )
106127end
107128
108129--- sync reload the file if it has been modified
@@ -146,7 +167,9 @@ function OrgFile:is_modified()
146167 local bufnr = self :bufnr ()
147168 if bufnr > - 1 then
148169 local cur_changedtick = vim .api .nvim_buf_get_changedtick (bufnr )
149- return cur_changedtick ~= self .metadata .changedtick
170+ if cur_changedtick ~= self .metadata .changedtick then
171+ return true
172+ end
150173 end
151174 local stat = vim .uv .fs_stat (self .filename )
152175 if not stat then
@@ -166,7 +189,7 @@ function OrgFile:parse(skip_if_not_modified)
166189 if skip_if_not_modified and self .root and not self :is_modified () then
167190 return self .root
168191 end
169- self .parser = self :_get_parser ( )
192+ self .parser = ts . get_parser ( self :bufnr (), ' org ' , {} )
170193 local trees = self .parser :parse ()
171194 self .root = trees [1 ]:root ()
172195 return self .root
@@ -185,7 +208,7 @@ function OrgFile:get_ts_matches(query, parent_node)
185208 local ts_query = ts_utils .get_query (query )
186209 local matches = {}
187210
188- for _ , match , _ in ts_query :iter_matches (parent_node , self :get_source (), nil , nil , { all = true }) do
211+ for _ , match , _ in ts_query :iter_matches (parent_node , self :bufnr (), nil , nil , { all = true }) do
189212 local items = {}
190213 for id , nodes in pairs (match ) do
191214 local name = ts_query .captures [id ]
@@ -215,7 +238,7 @@ function OrgFile:get_ts_captures(query, node)
215238 local ts_query = ts_utils .get_query (query )
216239 local matches = {}
217240
218- for _ , match in ts_query :iter_captures (node , self :get_source ()) do
241+ for _ , match in ts_query :iter_captures (node , self :bufnr ()) do
219242 table.insert (matches , match )
220243 end
221244 return matches
@@ -466,13 +489,13 @@ function OrgFile:get_node_text(node, range)
466489 return ' '
467490 end
468491 if range then
469- return ts .get_node_text (node , self :get_source (), {
492+ return ts .get_node_text (node , self :bufnr (), {
470493 metadata = {
471494 range = range ,
472495 },
473496 })
474497 end
475- return ts .get_node_text (node , self :get_source ())
498+ return ts .get_node_text (node , self :bufnr ())
476499end
477500
478501--- @param node ? TSNode
@@ -534,19 +557,22 @@ end
534557
535558--- @return number
536559function OrgFile :bufnr ()
537- local bufnr = utils . get_buffer_by_filename ( self .filename )
560+ local bufnr = self .buf
538561 -- Do not consider unloaded buffers as valid
539562 -- Treesitter is not working in them
540563 if bufnr > - 1 and vim .api .nvim_buf_is_loaded (bufnr ) then
541564 return bufnr
542565 end
543- return - 1
566+ local new_bufnr = vim .fn .bufadd (self .filename )
567+ vim .fn .bufload (new_bufnr )
568+ self .buf = new_bufnr
569+ return new_bufnr
544570end
545571
546572--- Return valid buffer handle or throw an error if it's not valid
547573--- @return number
548574function OrgFile :get_valid_bufnr ()
549- local bufnr = utils . get_buffer_by_filename ( self . filename )
575+ local bufnr = self : bufnr ( )
550576 if bufnr < 0 then
551577 error (' [orgmode] No valid buffer for file ' .. self .filename .. ' to edit' , 0 )
552578 end
@@ -784,7 +810,7 @@ function OrgFile:get_links()
784810 (link_desc) @link
785811 ]] )
786812
787- local source = self :get_source ()
813+ local source = self :bufnr ()
788814 for _ , node in ipairs (matches ) do
789815 table.insert (links , Hyperlink .from_node (node , source ))
790816 end
@@ -805,7 +831,7 @@ function OrgFile:get_footnote_references()
805831
806832 local footnotes = {}
807833 local processed_lines = {}
808- for _ , match in ts_query :iter_captures (self .root , self :get_source ()) do
834+ for _ , match in ts_query :iter_captures (self .root , self :bufnr ()) do
809835 local line_start , _ , line_end = match :range ()
810836 if not processed_lines [line_start ] then
811837 if line_start == line_end then
@@ -895,51 +921,14 @@ function OrgFile:_get_directive(directive_name)
895921end
896922
897923--- @private
898- --- @param lines string[]
899- --- @param bufnr ? number
900- function OrgFile :_update_lines (lines , bufnr )
901- self .lines = lines
902- self .content = table.concat (lines , ' \n ' )
903- self :parse ()
904- if bufnr then
905- self .metadata .changedtick = vim .api .nvim_buf_get_changedtick (bufnr )
906- end
907- local stat = vim .uv .fs_stat (self .filename )
908- if stat then
909- self .metadata .mtime = stat .mtime .nsec
910- self .metadata .mtime_sec = stat .mtime .sec
911- end
912- return self
913- end
914-
915- --- @private
916- --- @return vim.treesitter.LanguageTree
917- function OrgFile :_get_parser ()
918- local bufnr = self :bufnr ()
919-
920- if bufnr > - 1 then
921- -- Always get the fresh parser for the buffer
922- return ts .get_parser (bufnr , ' org' , {})
923- end
924-
925- -- In case the buffer got unloaded, go back to string parser
926- if not self .parser or self :is_modified () or type (self .parser :source ()) == ' number' then
927- return ts .get_string_parser (self .content , ' org' , {})
928- end
929-
930- return self .parser
931- end
932-
933- --- Get the ts source for the file
934- --- If there is a buffer, return buffer number
935- --- Otherwise, return the string content
936- --- @return integer | string
937- function OrgFile :get_source ()
938- local bufnr = self :bufnr ()
939- if bufnr > - 1 then
940- return bufnr
924+ --- Get all buffer lines, ensure empty buffer returns empty table
925+ --- @return string[]
926+ function OrgFile :_get_lines (bufnr )
927+ local lines = vim .api .nvim_buf_get_lines (bufnr , 0 , - 1 , false )
928+ if # lines == 1 and lines [1 ] == ' ' then
929+ lines = {}
941930 end
942- return self . content
931+ return lines
943932end
944933
945934return OrgFile
0 commit comments