@@ -22,14 +22,13 @@ local Promise = require('fittencode.fn.promise')
2222local Session = require (' fittencode.inline.session' )
2323local i18n = require (' fittencode.i18n' )
2424local Log = require (' fittencode.log' )
25- local Definitions = require (' fittencode.inline.definitions' )
2625local CtrlObserver = require (' fittencode.inline.ctrl_observer' )
2726local ProgressIndicator = require (' fittencode.fn.progress_indicator' )
28- local Observer = require (' fittencode.fn.observer' )
2927local Color = require (' fittencode.color' )
3028local LspServer = require (' fittencode.integrations.completion.lsp_server' )
29+ local StateMachine = require (' fittencode.fn.state_machine' )
3130
32- -- local Status = CtrlObserver.Status
31+ local Status = CtrlObserver .Status
3332-- local ProgressIndicatorObserver = CtrlObserver.ProgressIndicatorObserver
3433-- local TimingObserver = CtrlObserver.TimingObserver
3534
@@ -38,11 +37,13 @@ local LspServer = require('fittencode.integrations.completion.lsp_server')
3837--- @field rhs function
3938--- @field options ? table<string , any>
4039
40+ --- @class FittenCode.Observer
41+ --- @field update function
42+
4143--- @class FittenCode.Inline.Controller
4244--- @field observers FittenCode.Observer[]
4345--- @field sessions FittenCode.Inline.Session[]
4446--- @field filter_events table<string , boolean>
45- --- @field status_observer FittenCode.Inline.Status
4647--- @field keymaps FittenCode.Keymap[]
4748local Controller = {}
4849Controller .__index = Controller
@@ -63,8 +64,8 @@ function Controller:_initialize(options)
6364 self .current_session = nil
6465 self .filter_events = {}
6566 self .keymaps = {}
66- -- self.status_observer = Status.new()
67- -- self:add_observer (self.status_observer)
67+ self .status_observer = Status .new ()
68+ self :on (self .status_observer )
6869 -- self.pi = ProgressIndicator.new()
6970 -- self.progress_observer = ProgressIndicatorObserver.new({ pi = self.pi })
7071 -- self:add_observer(self.progress_observer)
@@ -116,6 +117,13 @@ function Controller:_initialize(options)
116117 self :edit_completion_cancel ({ vimev = args })
117118 end ,
118119 })
120+ vim .api .nvim_create_autocmd ({ ' BufEnter' , ' BufFilePost' , ' FileType' }, {
121+ group = vim .api .nvim_create_augroup (' FittenCode.Inline.ServiceUpdate' , { clear = true }),
122+ pattern = ' *' ,
123+ callback = function (args )
124+ self :sync_state ()
125+ end ,
126+ })
119127 if Config .integrations .completion .lsp_server then
120128 Log .debug (' Completion integration: LSP server enabled' )
121129 -- vim.lsp.enable('FittenCode')
@@ -139,53 +147,53 @@ function Controller:_initialize(options)
139147 })
140148 -- If {fn} returns an empty string, {key} is discarded/ignored.
141149 vim .on_key (function (key )
142- local buf = vim .api .nvim_get_current_buf ()
143150 self .filter_events = {}
144- if vim .api .nvim_get_mode ().mode :sub (1 , 1 ) == ' i' and self : is_enabled ( buf ) then
151+ if vim .api .nvim_get_mode ().mode :sub (1 , 1 ) == ' i' and not self . state : is ( ' disabled ' ) then
145152 if vim .tbl_contains (filtered , key ) and Config .inline_completion .disable_completion_when_delete then
146153 self .filter_events = { ' CursorMovedI' , ' TextChangedI' , }
147154 return
148155 end
149156 end
150157 end )
151- end
152158
153- --- @param observer FittenCode.Observer | function
154- function Controller :add_observer (observer )
155- if type (observer ) == ' function' then
156- local id = ' callback_observer' .. Fn .generate_short_id_as_string ()
157- observer = setmetatable ({
158- id = id ,
159- update = function (_ , ctrl , event )
160- observer (ctrl , event )
161- end
162- }, { __index = Observer })
163- end
164- self .observers [observer .id ] = observer
165- return observer
166- end
167-
168- --- @param identifier string | FittenCode.Observer
169- function Controller :remove_observer (identifier )
170- local id = type (identifier ) == ' string' and identifier or identifier .id
171- self .observers [id ] = nil
159+ -- 进入buffer检测类型或手动更改类型,决定 disabled/enabled
160+ -- current_session.id == source.id { != terminated } running/idle
161+ self .state = StateMachine .new ({
162+ initial = ' idle' ,
163+ transitions = {
164+ idle = { ' running' , ' disabled' },
165+ running = { ' idle' , ' disabled' },
166+ disabled = { ' idle' , ' running' },
167+ },
168+ })
169+ self .state :subscribe (function (value )
170+ self :emit ({
171+ ctrl = value .to ,
172+ })
173+ end )
172174end
173175
174- --- @param id string
175- function Controller :get_observer (id )
176- return self .observers [id ]
176+ function Controller :on (observer )
177+ table.insert (self .observers , observer )
177178end
178179
179- --- @param event FittenCode.Inline.Event
180- function Controller :notify_observers (event )
181- vim .tbl_map (function (observer )
182- observer :update (self , event )
183- end , self .observers )
180+ function Controller :off (observer )
181+ for i , obs in ipairs (self .observers ) do
182+ if obs == observer then
183+ table.remove (self .observers , i )
184+ break
185+ end
186+ end
184187end
185188
186- --- @param event FittenCode.Inline.Event
187- function Controller :_emit (event )
188- self :notify_observers (event )
189+ function Controller :emit (...)
190+ for _ , observer in ipairs (self .observers ) do
191+ if type (observer ) == ' function' then
192+ observer (... )
193+ elseif observer .update then
194+ observer :update (... )
195+ end
196+ end
189197end
190198
191199function Controller :has_completions ()
@@ -264,7 +272,7 @@ function Controller:trigger_inline_suggestion(options)
264272 local force = (options .force == nil ) and false or options .force
265273
266274 local is_insert_mode = vim .api .nvim_get_mode ().mode :sub (1 , 1 ) == ' i'
267- local is_enabled = self : is_enabled ( buf )
275+ local is_enabled = not self . state : is ( ' disabled ' )
268276 local has_access_token = api_key_manager :has_fitten_access_token ()
269277 local is_filtered_event = options .vimev and vim .tbl_contains (self .filter_events , options .vimev .event )
270278 local is_within_the_line = Config .inline_completion .disable_completion_within_the_line and check_is_within_the_line (position )
@@ -287,33 +295,32 @@ function Controller:trigger_inline_suggestion(options)
287295 id = assert (Fn .generate_short_id (13 )),
288296 trigger_inline_suggestion = function (...) self :trigger_inline_suggestion_auto (... ) end ,
289297 is_outdated = function (s )
290- if s .id ~= self .current_session .id then
298+ if not self . current_session or s .id ~= self .current_session .id then
291299 Log .debug (' latest_session id = {}, other id = {}' , self .current_session .id , s .id )
292300 return true
293301 end
294302 return false
295303 end
296304 })
305+ self .current_session :on (function (value )
306+ local id = (self .current_session and not self .current_session :is_terminated ()) and self .current_session .id
307+ if not self .state :is (' disabled' ) then
308+ if not id then
309+ self .state :transition (' idle' )
310+ elseif id == value .id then
311+ self .state :transition (' running' )
312+ end
313+ end
314+ self :emit ({
315+ ctrl = self .state :state (),
316+ current_session_id = id ,
317+ session = value
318+ })
319+ end )
297320
298321 return self .current_session :send_completions ()
299322end
300323
301- -- ---@param data FittenCode.Inline.Event.Data
302- -- function Controller:on_session_event(data)
303- -- if data.session_event == SESSION_EVENT.CREATED then
304- -- self:_emit({ event = CONTROLLER_EVENT.INLINE_RUNNING, data = { id = data.id } })
305- -- self:_emit({ event = CONTROLLER_EVENT.SESSION_ADDED, data = { id = data.id } })
306- -- elseif data.session_event == SESSION_EVENT.TERMINATED then
307- -- Log.debug('Controller received session terminated event, event session id = {}, selected_session_id = {}', data.id, self.selected_session_id)
308- -- self:_emit({ event = CONTROLLER_EVENT.SESSION_DELETED, data = { id = data.id } })
309- -- self.sessions[data.id] = nil
310- -- if not self.selected_session_id or self.selected_session_id == data.id then
311- -- self.selected_session_id = nil
312- -- self:_emit({ event = CONTROLLER_EVENT.INLINE_IDLE, data = { id = data.id } })
313- -- end
314- -- end
315- -- end
316-
317324--- @return FittenCode.Inline.Session ?
318325function Controller :get_active_session ()
319326 local session = self :get_current_session ()
@@ -327,15 +334,30 @@ function Controller:get_current_session()
327334 return self .current_session
328335end
329336
330- --- @param buf integer
331- function Controller :is_enabled (buf )
337+ --- @param buf integer ?
338+ function Controller :should_enable (buf )
339+ if buf == nil then
340+ buf = vim .api .nvim_get_current_buf ()
341+ end
332342 local filebuf = true
333343 if Config .inline_completion .disable_completion_when_nofile_buffer then
334344 filebuf = F .is_filebuf (buf )
335345 end
336346 return Config .inline_completion .enable and filebuf and not self :is_ft_disabled (buf )
337347end
338348
349+ function Controller :sync_state ()
350+ if self :should_enable () then
351+ if self .current_session and not self .current_session :is_terminated () then
352+ self .state :transition (' running' )
353+ else
354+ self .state :transition (' idle' )
355+ end
356+ else
357+ self .state :transition (' disabled' )
358+ end
359+ end
360+
339361--- @param msg string
340362--- @param timeout number
341363--- @param timestamp number
@@ -459,11 +481,19 @@ function Controller:set_suffix_permissions(enable, suffixes)
459481 end
460482 end
461483 Config .disable_specific_inline_completion .suffixes = vim .tbl_keys (suffix_map )
484+ self :sync_state ()
462485end
463486
464- --- @return FittenCode.Inline.Status
465487function Controller :get_status ()
466- return self .status_observer
488+ return self .status_observer :get_snapshot ()
489+ -- if self:is_enabled() then
490+ -- if self.current_session and not self.current_session:is_terminated() then
491+ -- return 'running'
492+ -- end
493+ -- return 'idle'
494+ -- else
495+ -- return 'disabled'
496+ -- end
467497end
468498
469499return Controller
0 commit comments