
    PL
j                    ,   d Z ddlmZ ddlZddlZddlZddlmZ ddlm	Z	m
Z
mZmZmZmZmZ ddlmZmZ ddlmZmZmZmZmZmZmZmZmZmZmZ  ej        d          Z d	Z!d
Z"dZ#dZ$dZ%dZ&dZ'dZ(d#dZ)d$dZ*d%dZ+ G d d          Z,d&dZ-d'd!Z.g d"Z/dS )(u~  Async LSP client over stdin/stdout.

One :class:`LSPClient` corresponds to one ``(language_server, workspace_root)``
pair — exactly what OpenCode keys clients on, and the same shape Claude
Code uses.  The client owns a child process, drives the JSON-RPC
exchange, and exposes:

- :meth:`open_file` / :meth:`change_file` — text document sync
- :meth:`wait_for_diagnostics` — block until the server emits fresh
  diagnostics for a specific file (or a timeout fires)
- :meth:`diagnostics_for` — read the current per-file diagnostic store
- :meth:`shutdown` — graceful close + SIGTERM/SIGKILL fallback

The class is designed for async use from a single asyncio event loop.
The :class:`agent.lsp.manager.LSPService` runs an event loop in a
background thread so the synchronous file_operations layer can call
into it via :func:`agent.lsp.manager.LSPService.touch_file`.

Implementation notes:

- Push diagnostics are stored per-URI in :attr:`_push_diagnostics` from
  ``textDocument/publishDiagnostics`` notifications.  Pull diagnostics
  go in :attr:`_pull_diagnostics`.  The merged view dedupes by content.

- Whole-document sync.  Even when the server advertises incremental
  sync, we send a single ``contentChanges`` entry replacing the
  entire document.  Pretending to be incremental while sending a
  full replacement is well-tolerated by every major server and saves
  range bookkeeping.  See OpenCode's ``client.ts:584-659`` for the
  same trick.

- The "touch-file dance": every ``open_file`` call also fires a
  ``workspace/didChangeWatchedFiles`` notification (CREATED on the
  first open, CHANGED thereafter).  Some servers (clangd, eslint)
  only re-scan when this notification fires, even though the LSP spec
  doesn't strictly require it.

- ``ContentModified`` (-32801) errors get retried with exponential
  backoff up to 3 times.  This matches Claude Code's
  ``LSPServerInstance.sendRequest``.
    )annotationsN)Path)Any	AwaitableCallableDictListOptionalSet)quoteunquote)ERROR_CONTENT_MODIFIEDERROR_METHOD_NOT_FOUNDLSPProtocolErrorLSPRequestErrorclassify_messageencode_messagemake_error_responsemake_notificationmake_requestmake_responseread_messagezagent.lsp.clientg     F@g      @g      $@g      @g333333?g      ?         ?pathstrreturnc                    t           j                            |           }t           j        dk    r0|                    dd          }|                    d          sd|z   }dt          |d          z   S )u   Return ``file://`` URI for an absolute filesystem path.

    Mirrors Node's ``pathToFileURL`` — handles spaces, unicode, and
    Windows drive letters (``C:\foo`` → ``file:///C:/foo``).
    nt\/file://z/:)safe)osr   abspathnamereplace
startswithr   )r   abs_paths     4/home/kuhnn/.hermes/hermes-agent/agent/lsp/client.pyfile_urir+   P   sp     wt$$H	w$ ##D#..""3'' 	&X~HuXD11111    uric                R   |                      d          s| S | t          d          d         }t          j        dk    r>|                     d          r)t          |          dk    r|d         dk    r
|dd         }t          j                            t          |                    S )zInverse of :func:`file_uri`.r"   Nr   r!      :   )r(   lenr$   r&   r   normpathr   )r-   raws     r*   uri_to_pathr5   `   s    >>)$$ 

c)nnoo
C	w$3>>#..3s88a<<CFcMM!""g7GCLL)))r,   textDict[str, int]c                    | sdddS |                      d          }t          |          dz
  }|rt          |d                   nd}|                     d          r|dz   ddS ||dS )zReturn the LSP Position at the end of ``text``.

    Used to construct a single-range "replace whole document" change
    for ``textDocument/didChange`` regardless of the server's declared
    sync mode.
    r   line	characterF)keependsr1   )
)
splitlinesr2   endswith)r6   lines	last_linelast_cols       r*   _end_positionrE   j   s      +***OOUO++EE

QI!&-s59~~~AH }}\"" 7!AA666H555r,   c                  Z   e Zd ZdZddddddJdZedKd            ZedLd            ZdMdZdMdZ	dMdZ
dMdZdMdZedNd            ZdMd ZdMd!ZdOd%ZdPd(ZdQd)ZdRd,ZdSd/ZdTd1ZdUd2ZdVd3ZdWd4ZdWd5ZdWd6ZdWd7ZdWd8ZdWd9ZdXd:Zd;d<dYd?Z dZd@Z!dZdAZ"dBdCd[dFZ#d\dGZ$d]dIZ%dS )^	LSPClienta  Async LSP client tied to one server process and one workspace root.

    Lifecycle:

        c = LSPClient(server_id, workspace_root, command, args, init_options)
        await c.start()       # spawn + initialize
        ver = await c.open_file("/path/to/foo.py")
        await c.wait_for_diagnostics("/path/to/foo.py", ver)
        diags = c.diagnostics_for("/path/to/foo.py")
        await c.shutdown()
    NF)envcwdinitialization_optionsseed_diagnostics_on_first_push	server_idr   workspace_rootcommand	List[str]rH   Optional[Dict[str, str]]rI   Optional[str]rJ   Optional[Dict[str, Any]]rK   boolr   Nonec               d   || _         || _        t          |          | _        || _        |p|| _        |pi | _        || _        d | _        d | _	        d | _
        d| _        i | _        | j        | j        | j        | j        | j        | j        d| _        d| j        i| _        i | _        i | _        i | _        i | _        i | _        t7                      | _        i | _        d| _        d | _        d| _         d| _!        tE          j#                    | _$        d| _%        tE          j#                    | _&        d S )Nr   )zwindow/workDoneProgress/createzworkspace/configurationzclient/registerCapabilityzclient/unregisterCapabilityzworkspace/workspaceFolderszworkspace/diagnostic/refreshztextDocument/publishDiagnosticsstoppedr1   F)'rL   rM   list_command_env_cwd_init_options_seed_first_push_proc_stderr_task_reader_task_next_id_pending_handle_work_done_create_handle_workspace_configuration_handle_register_capability_handle_unregister_capability_handle_workspace_folders_handle_diagnostic_refresh_request_handlers_handle_publish_diagnostics_notification_handlers_files_push_diagnostics_pull_diagnostics
_published_published_versionset_first_push_seen_diagnostic_registrations_state_initialize_result
_sync_kind	_stoppingasyncioEvent_push_event_push_counter_registration_event)selfrL   rM   rN   rH   rI   rJ   rK   s           r*   __init__zLSPClient.__init__   sK    #,W	)>	39r > <@
4848 35
 /3.K'+'K)-)I+/+M*.*H,0,KN
 N
 .t/OI
# 24BDBD,. 35*-%%DF& %<@ $ #=??
  $+=??   r,   c                F    | j         dk    o| j        d uo| j        j        d u S )Nrunning)rs   r]   
returncoder|   s    r*   
is_runningzLSPClient.is_running   s+    {i'dDJd,BdtzG\`dGddr,   c                    | j         S N)rs   r   s    r*   statezLSPClient.state   s
    {r,   c                  K   | j         dv rdS d| _         	 |                                  d{V  |                                  d{V  d| _         dS # t          $ r# d| _         |                                  d{V   w xY w)u   Spawn the server and complete the initialize handshake.

        Raises any exception encountered during spawn/init.  On failure
        the process is killed and the client is left in state
        ``"error"`` — re-call ``start()`` to retry.
        >   r   startingNr   r   error)rs   _spawn_initialize	Exception_cleanup_processr   s    r*   startzLSPClient.start   s       ;111F 	++--""$$$$$$$$$#DKKK 	 	 	!DK'')))))))))	s   ;A -B c           	     r  K   t          t          j                  }| j        r|                    | j                   	 t          j        | j        d         g| j        dd          R t
          j        j	        t
          j        j	        t
          j        j	        || j
        d d {V | _        n4# t          $ r'}t          d| j        d          d| d          |d }~ww xY wt          j        |                                           | _        t          j        |                                           | _        d S )Nr   r1   )stdinstdoutstderrrH   rI   zLSP server binary not found: z ())dictr$   environrY   updaterw   create_subprocess_execrX   
subprocessPIPErZ   r]   FileNotFoundErrorr   create_task_drain_stderrr^   _reader_loopr_   )r|   rH   es      r*   r   zLSPClient._spawn   sV     2:9 	"JJty!!!	&=a  qrr"    (-).).I           DJJ ! 	 	 	"Ha0@HHAHHH 	 $/0B0B0D0DEE#/0A0A0C0CDDs   A.B- -
C7"CCc                h  K   | j         | j         j        d S 	 	 | j         j                                         d {V }|sd S |                    dd                                          }|r)t
                              d| j        |d d                    }# t          j	        t          f$ r Y d S w xY w)NTutf-8r'   )errorsz[%s] stderr: %si  )r]   r   readlinedecoderstriploggerdebugrL   rw   CancelledErrorOSError)r|   r:   r6   s      r*   r   zLSPClient._drain_stderr  s      :!2!:F		Q!Z.7799999999 E{{79{==DDFF QLL!2DND$KPPPQ &0 	 	 	DD	s   'B AB B10B1c                  K   | j         | j         j        d S 	 	 t          | j         j                   d {V }|!t                              d| j                   nt          |          \  }}|dk    r|                     ||           nm|dk    r)t          j	        | 
                    ||                     n>|dk    r|                     ||           n!t                              d| j        |           nO# t          $ r+}t                              d| j        |           Y d }~nd }~wt          j        t          f$ r Y nw xY wt!          | j                                                  D ]8}|                                s"|                    t          d                     9| j                                         d S # t!          | j                                                  D ]8}|                                s"|                    t          d                     9| j                                         w xY w)	NTz![%s] server closed stdout cleanlyresponserequestnotificationz![%s] dropping invalid message: %rz&[%s] protocol error in reader loop: %szserver connection closed)r]   r   r   r   r   rL   r   _dispatch_responserw   r   _dispatch_request_dispatch_notificationwarningr   r   r   rW   ra   valuesdoneset_exceptionclear)r|   msgkindkeyr   futs         r*   r   zLSPClient._reader_loop  sk     :!2!:F	"]():;;;;;;;;;LL!DdnUUU,S11	c:%%++C5555Y&&'(>(>sC(H(HIIII^++//S9999NN#FX[\\\]    	X 	X 	XNNCT^UVWWWWWWWW&0 	 	 	D	 DM002233 T Txxzz T%%&67Q&R&RSSSM!!!!! DM002233 T Txxzz T%%&67Q&R&RSSSM!!!!s=   C C: 9G :
E!D*%G *EG EG A:H=c                v  K   t          | j                  | j        t          j                    dt          | j                  dg| j        ddiddddiddidddddddd	ddd
ddddgidddddddgiddii ddidddgidd}t          j        |                     d|          t                     d {V }|| _	        | 
                    |                    d          pi           | _        |                     di            d {V  | j        r%|                     dd| j        i           d {V  d S d S )N	workspacer&   r-   workDoneProgressTdynamicRegistrationrefreshSupportF)configurationworkspaceFoldersdidChangeWatchedFilesdiagnostics)r   didOpen	didChangedidSavewillSavewillSaveWaitUntil)r   relatedDocumentSupportvalueSetr1   r/   )relatedInformation
tagSupportversionSupportcodeDescriptionSupportdataSupportcontentFormatmarkdown	plaintextlinkSupport!hierarchicalDocumentSymbolSupport)synchronization
diagnosticpublishDiagnosticshover
definition
referencesdocumentSymbolpositionEncodingszutf-16)windowr   textDocumentgeneral)rootUrirootPath	processIdr   initializationOptionscapabilities
initializetimeoutr   initializedz workspace/didChangeConfigurationsettings)r+   rM   r$   getpidr[   rw   wait_for_send_requestINITIALIZE_TIMEOUTrt   _extract_sync_kindgetru   _send_notification)r|   paramsresults      r*   r   zLSPClient._initialize<  s      344+$Xd6I-J-JKK! &*%7-t4%)(,.CT-J$4e#<	  05#'%)#'$)-2( ( 0426# #
 /3'1Aq6&:*.26',+ + .
K/HI#0$"7"$'JD&Q/! !2 0(<C" "+
 +
Z '|V44&
 
 
 
 
 
 
 
 
 #)11&**^2L2L2RPRSS%%mR888888888 	 ))2T/0          		 	r,   r   r   intc                    |                      d          }t          |t                    r|S t          |t                    r,|                     d          }t          |t                    r|S dS )NtextDocumentSyncchanger1   )r   
isinstancer   r   )r   syncr   s      r*   r   zLSPClient._extract_sync_kind{  sl     233dC   	KdD!! 	XXh''F&#&& qr,   c                  K   | j         rdS d| _         	 | j        r	 t          j        |                     dd          d           d{V  n"# t          j        t          t          f$ r Y nw xY w	 |                     dd           d{V  n# t          $ r Y nw xY wd| _
        |                                  d{V  dS # d| _
        |                                  d{V  w xY w)zBest-effort graceful shutdown.

        Sends ``shutdown`` + ``exit``, then SIGTERMs/SIGKILLs the
        process if it doesn't exit cleanly.  Idempotent.
        NTshutdowng       @r   exitrV   )rv   r   rw   r   r   TimeoutErrorr   r   r   r   rs   r   r   s    r*   r   zLSPClient.shutdown  sP      > 	F	* !*4+=+=j$+O+OY\]]]]]]]]]]],o?OP   D11&$??????????    D $DK''))))))))))) $DK''))))))))))sR   C  0A C  A,)C  +A,,C  0B C  
BC  BC   #C#c                6  K   | j         ]| j                                         sD| j                                          	 | j          d {V  n# t          j        t
          f$ r Y nw xY w| j        ]| j                                        sD| j                                         	 | j         d {V  n# t          j        t
          f$ r Y nw xY w| j        }d | _        |d S |j        	 |	                                 	 t          j
        |                                t                     d {V  d S # t          j        $ rG 	 |                                 |                                 d {V  n# t          $ r Y nw xY wY d S Y d S w xY w# t          $ r Y d S w xY wd S )Nr   )r_   r   cancelrw   r   r   r^   r]   r   	terminater   waitSHUTDOWN_GRACEr   killProcessLookupError)r|   procs     r*   r   zLSPClient._cleanup_process  s!     (1B1G1G1I1I($$&&&'''''''''*I6   (1B1G1G1I1I($$&&&'''''''''*I6   z
<F?"   !*499;;OOOOOOOOOOOO+   		"iikk))))))))-    *)) &    #"s}   A A$#A$!B/ /CC%F :3D/ /F?.E.-F.
E;8F:E;;F>F F FF 
FFmethodr   r   c           	     
  K   | j         *| j         j        | j         j                                        rt          d|d          t	          j                    }| j        }| xj        dz  c_        |                                }|| j        |<   	 | j         j        	                    t          t          |||                               | j         j                                         d {V  nP# t          t          t          f$ r6}| j                            |d            t          d|d|           |d }~ww xY w	 | d {V 	 | j                            |d            S # | j                            |d            w xY w)Nzcannot send z: stdin closedr1   zsend failed for : )r]   r   
is_closingr   rw   get_running_loopr`   create_futurera   writer   r   drainBrokenPipeErrorConnectionResetErrorr   pop)r|   r  r   loopreq_idr   r   s          r*   r   zLSPClient._send_request  s     :!1!9TZ=M=X=X=Z=Z!9"#J&#J#J#JKKK'))"0022 #f	NJ"">,vvv2V2V#W#WXXX*"((**********!5w? 	N 	N 	NMfd+++"#Ef#E#E!#E#EFFAM	N	,999999Mfd++++DMfd++++s%   AC0 0D=1D88D=E% %Fr   floatc               R  K   t          t          dz             D ]}	 t          j        |                     ||          |           d{V c S # t
          $ rK}|j        t          k    r5|t          k     r*t          j        t          d|z  z             d{V  Y d}~ d}~ww xY wdS )u   Send a request, retrying on ``ContentModified`` (-32801).

        Other errors propagate.  The retry policy matches Claude Code's
        ``LSPServerInstance.sendRequest`` — 3 attempts with delays
        0.5s, 1.0s, 2.0s.
        r1   r   Nr/   )
rangeMAX_CONTENT_MODIFIED_RETRIESrw   r   r   r   coder   sleepRETRY_BASE_DELAY)r|   r  r   r   attemptr   s         r*   _send_request_with_retryz"LSPClient._send_request_with_retry  s       9A=>> 	 	G$-d.@.@.P.PZabbbbbbbbbbbb"   6333B^8^8^!-(8AL(IJJJJJJJJJHHHH		 	s   /A
B$A BBB$c                  K   | j         *| j         j        | j         j                                        rd S 	 | j         j                            t	          t          ||                               | j         j                                         d {V  d S # t          t          t          f$ r-}t                              d| j        ||           Y d }~d S d }~ww xY w)Nz[%s] notify %s failed: %s)r]   r   r  r	  r   r   r
  r  r  r   r   r   rL   )r|   r  r   r   s       r*   r   zLSPClient._send_notification  s      :!1!9TZ=M=X=X=Z=Z!9F	QJ"">2CFF2S2S#T#TUUU*"((***********!5w? 	Q 	Q 	QLL4dnfaPPPPPPPPP	Qs   AB C."CCr  r   c                j  K   | j         *| j         j        | j         j                                        rd S 	 | j         j                            t	          t          ||                               | j         j                                         d {V  d S # t          t          t          f$ r Y d S w xY wr   )
r]   r   r  r	  r   r   r
  r  r  r   )r|   r  r   s      r*   _send_responsezLSPClient._send_response  s      :!1!9TZ=M=X=X=Z=Z!9F	J"">-2O2O#P#PQQQ*"((***********!5w? 	 	 	DD	s   AB B21B2r  messagec           	     l  K   | j         *| j         j        | j         j                                        rd S 	 | j         j                            t	          t          |||                               | j         j                                         d {V  d S # t          t          t          f$ r Y d S w xY wr   )
r]   r   r  r	  r   r   r
  r  r  r   )r|   r  r  r  s       r*   _send_error_responsezLSPClient._send_error_response  s      :!1!9TZ=M=X=X=Z=Z!9F	J"">2EfdT[2\2\#]#]^^^*"((***********!5w? 	 	 	DD	s   AB B32B3r   c                   | j                             |          }||                                rd S d|v r|d         pi }|                    t	          t          |                    dd                    t          |                    dd                    |                    d                               d S |                    |                    d                     d S )	Nr   r   r  unknowndata)r  r  r"  r   )ra   r   r   r   r   r   r   
set_result)r|   r  r   r   errs        r*   r   zLSPClient._dispatch_response  s    m'';#((**;Fc>>g,$"CSWWVV4455	9 = =>>       NN3778,,-----r,   c                  K   |                     dd          }|                     d          }| j                             |          }|'|                     |t          d|            d {V  d S 	  ||           d {V }nZ# t          $ rM}t
                              d| j        ||           |                     |dd|            d {V  Y d }~d S d }~ww xY w|                     ||           d {V  d S )Nr   r   zmethod not found: z"[%s] request handler %s failed: %sr   zhandler failed: )	r   rh   r  r   r   r   r   rL   r  )r|   r  r   r  r   handlerr   r   s           r*   r   zLSPClient._dispatch_request  sQ     2&&""(,,V44?++F4JLiagLiLijjjjjjjjjF	"76??******FF 	 	 	NN?QWYZ[[[++FF<Rq<R<RSSSSSSSSSFFFFF	 !!&&11111111111s   2B 
CACCc                    | j                             |          }|d S 	  ||                    d                     d S # t          $ r-}t                              d| j        ||           Y d }~d S d }~ww xY w)Nr   z'[%s] notification handler %s failed: %s)rj   r   r   r   r   rL   )r|   r  r   r'  r   s        r*   r   z LSPClient._dispatch_notification  s    -11&99?F	_GCGGH%%&&&&& 	_ 	_ 	_LLBDNTZ\]^^^^^^^^^	_s   A   
A7
"A22A7c                
   K   d S r    r|   r   s     r*   rb   z"LSPClient._handle_work_done_create*        tr,   c                  K   t          |t                    sd gS |                    d          pg }g }|D ]}t          |t                    s|                    d            -|                    d          }|r| j        s|                    | j        pd            h| j        }t          |                              d          D ]&}t          |t                    r||v r	||         }$d } |                    |           |S )Nitemssection.)r   r   r   appendr[   r   split)r|   r   r.  outitemr/  curparts           r*   rc   z)LSPClient._handle_workspace_configuration.  s       &$'' 	6M

7##)r 	 	DdD)) 

4   hhy))G $"4 

4-5666)CG**3//  c4(( TS[[d)CCCJJsOOOO
r,   c                \  K   t          |t                    sd S |                    d          pg D ]z}t          |t                    s|                    d          }|                    d          }|dk    r2|r0|| j        t	          |          <   | j                                         {d S )Nregistrationsr  idtextDocument/diagnostic)r   r   r   rr   r   r{   rp   )r|   r   regr  reg_ids        r*   rd   z%LSPClient._handle_register_capabilityG  s      &$'' 	4::o..4" 	/ 	/Cc4(( WWX&&FWWT]]F222v2>A.s6{{;(,,...tr,   c                  K   t          |t                    sd S |                    d          pg D ]W}t          |t                    s|                    d          }|r(| j                            t          |          d            Xd S )Nunregisterationsr9  )r   r   r   rr   r  r   )r|   r   unregr<  s       r*   re   z'LSPClient._handle_unregister_capabilityT  s      &$'' 	4ZZ 2339r 	F 	FEeT** YYt__F F.223v;;EEEtr,   c                6   K   dt          | j                  dgS )Nr   r   )r+   rM   r+  s     r*   rf   z#LSPClient._handle_workspace_folders_  s"      $Xd6I-J-JKKLLr,   c                
   K   d S r   r*  r+  s     r*   rg   z$LSPClient._handle_diagnostic_refreshb  r,  r,   c                $   t          |t                    sd S |                    d          }t          |t                    sd S t	          |          }|                    d          pg }t          |t
                    sg }|                    d          }t          j                                                    }| j	        rX|| j
        vrO| j
                            |           || j        |<   || j        |<   t          |t                    r
|| j        |<   d S || j        |<   || j        |<   t          |t                    r
|| j        |<   | j
                            |           | xj        dz  c_        | j                                         d S )Nr-   r   versionr1   )r   r   r   r   r5   rW   rw   get_event_looptimer\   rq   addrl   rn   r   ro   rz   ry   rp   )r|   r   r-   r   r   rC  	loop_times          r*   ri   z%LSPClient._handle_publish_diagnosticsj  s   &$'' 	Fjj#s## 	F3jj//52+t,, 	K**Y''*,,1133	  
	T1F%F%F
 !%%d++++6D"4($-DOD!'3'' 807'-F'2t$ )gs## 	4,3D#D)!!$''' 	ar,   r   )language_idr   rH  c          	       K   | j         st          d          t          j                            |          }	 t          |                              dd          }n(# t          $ r}t          d| d|           |d}~ww xY wt          |          }| j	        
                    |          }||                     dd	|d
dgi           d{V  |d         dz   }|d         }	| j        d
k    rdddt          |	          d|dg}
nd|ig}
|                     d||d|
d           d{V  ||d| j	        |<   |S |                     dd	|ddgi           d{V  | j                            |d           | j                            |d           | j                            |d           | j                            |d           |                     dd||d|di           d{V  d|d| j	        |<   dS )zSend didOpen (first time) or didChange (subsequent) for ``path``.

        Returns the new document version number that the agent's
        ``wait_for_diagnostics`` should match against.
        zclient not runningr   r'   )encodingr   zcannot read r  Nzworkspace/didChangeWatchedFileschangesr/   )r-   typerC  r1   r6   r   r9   )r   end)r  r6   ztextDocument/didChange)r-   rC  )r   contentChanges)rC  r6   ztextDocument/didOpenr   )r-   
languageIdrC  r6   )r   r   r$   r   r%   r   	read_textr   r+   rk   r   r   ru   rE   rl   r  rm   rn   ro   )r|   r   rH  r)   r6   r   r-   existingnew_versionold_textcontent_changess              r*   	open_filezLSPClient.open_file  s       	9"#78887??4((	J>>++WY+OODD 	J 	J 	J"#A(#A#Aa#A#ABBI	J x  ;??8,,))1S!4456         #9-1K'H!## /0a%@%@#0#:#:" " !% # %+D>"2))(,/K$H$H&5          1<T$J$JDK! %%-a0012
 
 	
 	
 	
 	
 	
 	
 	
 	""8T222""8T222Hd+++##Hd333%%""-  	! !

 

 
	
 
	
 
	
 
	
 
	
 
	
 
	
 -.t < <Hqs   $A 
B(A>>Bc                   K   | j         sdS t          j                            |          }|                     dddt          |          ii           d{V  dS )z>Send didSave for ``path``.  Some linters re-scan only on save.NztextDocument/didSaver   r-   )r   r$   r   r%   r   r+   )r|   r   r)   s      r*   	save_filezLSPClient.save_file  s       	F7??4((%%"eXh%7%789
 
 	
 	
 	
 	
 	
 	
 	
 	
 	
r,   c                T  K   	 ddt          t          j                            |                    ii}|                     d|t
                     d{V }nK# t          t          t          j	        f$ r,}t                              d| j        |           Y d}~dS d}~ww xY wt          |t                    sdS |                    d          }t          |t                     r'|| j        t          j                            |          <   |                    d          }t          |t                    rq|                                D ]^\  }}t          |t                    s|                    d          }	t          |	t                     r|	| j        t'          |          <   ]dS dS )	zSend ``textDocument/diagnostic`` for one file.

        Stores results into :attr:`_pull_diagnostics`.  Silently
        no-ops on errors (server may not support the pull endpoint).
        r   r-   r:  r   Nz([%s] document diagnostic pull failed: %sr.  relatedDocuments)r+   r$   r   r%   r  DIAGNOSTICS_REQUEST_TIMEOUTr   r   rw   r   r   r   rL   r   r   r   rW   rm   r.  r5   )
r|   r   r   r   r   r.  relatedr-   sub	sub_itemss
             r*   _pull_document_diagnosticsz$LSPClient._pull_document_diagnostics  s     	1F1F(G(G H&F  88)3 9        FF
  !173GH 	 	 	LLCT^UVWWWFFFFF	 &$'' 	F

7##eT"" 	B<AD"27??4#8#89**/00gt$$ 	I#MMOO I IS!#t,, GGG,,	i.. I?HD*;s+;+;<	I 	II Is   AA B 4!BB document)moderC  r`  c                 K   |dk    rt           nt          }t          j                                                    |z   }t
          j                            |          }	 |t          j                                                    z
  }|dk    rdS t          j        | 	                    |                    }t          j        | 
                    |||                    }	t          j        ||	h|t          j                   d{V \  }
}|D ]}|                                 |D ]'}	 | d{V  # t          j        t          f$ r Y $w xY w| j                            |          }|| j        v r
|||k    rdS || j        v rdS ))ud  Wait for the server to publish diagnostics for ``path`` at ``version``.

        ``mode`` is ``"document"`` (5s budget, document pulls) or
        ``"full"`` (10s budget, also workspace pulls).  Best-effort —
        returns silently on timeout.  Does NOT throw if the server
        doesn't support pull diagnostics; we still get the push side.
        fullTr   N)r   return_when)DIAGNOSTICS_FULL_WAITDIAGNOSTICS_DOCUMENT_WAITrw   rD  rE  r$   r   r%   r   r^  _wait_for_fresh_pushr   FIRST_COMPLETEDr   r   r   ro   r   rn   rm   )r|   r   rC  r`  budgetdeadliner)   	remaining	pull_task	push_taskr   pendingt	current_vs                 r*   wait_for_diagnosticszLSPClient.wait_for_diagnostics  s      +/&..&&>W)++0022V;7??4((	 7#9#;#;#@#@#B#BBIA~~  +D,K,KH,U,UVVI+D,E,EhPWYb,c,cddI"),I&!#3# # #      MD'
   



  GGGGGGGG.	:   D /33H==I4?**!Y'%9%9 4111?	s   )D22E
Ec                  K   t          j                                                    |z   }| j        }	 | j                            |          }|| j        v r|||k    r| j        }t          j                                                    t          z   }| j        |k    r|t          j                                                    z
  }	|	dk    rnn| j        	                                 	 t          j
        | j                                        |	           d{V  n# t           j        $ r Y nw xY w| j        |k    dS |t          j                                                    z
  }	|	dk    rdS | j        |k    r	| j        }N| j        	                                 	 t          j
        | j                                        t          |	d                     d{V  n# t           j        $ r Y w xY w)zEWait until a publishDiagnostics arrives for ``path`` at ``version``+.TNr   r   r   )rw   rD  rE  rz   ro   r   rn   PUSH_DEBOUNCEry   r   r   r   r   min)
r|   r   rC  r   ri  baselinero  debounce_baselinedebounce_deadlinerj  s
             r*   rf  zLSPClient._wait_for_fresh_pushA  s(     )++0022W<%	/33D99It&&I,=gAUAU
 %)$6!$+$:$<$<$A$A$C$Cm$S!(,=== 1G4J4L4L4Q4Q4S4S SI A~~$**,,,%.t/?/D/D/F/FPYZZZZZZZZZZZ"/    (,===  7#9#;#;#@#@#B#BBIA~~!H,,  -""$$$&t'7'<'<'>'>IWZH[H[\\\\\\\\\\\'   =	s%   &3D D,+D,AG G/.G/List[Dict[str, Any]]c                    t           j                            |          }| j                            |          pg }| j                            |          pg }t          ||          S )a  Return current merged + deduped diagnostics for one file.

        Diagnostics from push and pull stores are concatenated and
        deduplicated by ``(severity, code, message, range)`` content
        key.  Empty list if the server hasn't published anything.
        )r$   r   r%   rl   r   rm   _dedupe)r|   r   r)   pushpulls        r*   diagnostics_forzLSPClient.diagnostics_forf  s]     7??4((%))(339r%))(339rtT"""r,   )rL   r   rM   r   rN   rO   rH   rP   rI   rQ   rJ   rR   rK   rS   r   rT   )r   rS   )r   r   )r   rT   )r   r   r   r   )r  r   r   r   r   r   )r  r   r   r   r   r  r   r   )r  r   r   r   r   rT   )r  r   r   r   r   rT   )r  r   r  r   r  r   r   rT   )r  r   r   r   r   rT   )r  r   r   r   r   rT   )r  r   r   r   r   rT   )r   r   r   r   )r   r   r   rT   )r   r   rH  r   r   r   )r   r   r   rT   )r   r   rC  r   r`  r   r   rT   )r   r   rC  r   r   r  r   rT   )r   r   r   rw  )&__name__
__module____qualname____doc__r}   propertyr   r   r   r   r   r   r   staticmethodr   r   r   r   r  r   r  r  r   r   r   rb   rc   rd   re   rf   rg   ri   rU  rW  r^  rp  rf  r|  r*  r,   r*   rG   rG   ~   s&       
 
, )-!;?/4K3 K3 K3 K3 K3 K3Z e e e Xe    X   &E E E E4   " " " ":= = = =~    \* * * *.   F, , , ,&    Q Q Q Q      . . . . 2 2 2 2_ _ _ _      2   	 	 	 	M M M M   $ $ $ $T @K H H H H H HT
 
 
 
I I I IJ 1 1 1 1 1 1j# # # #J
# 
# 
# 
# 
# 
#r,   rG   listsrw  c                     t                      }g }| D ][}|D ]V}t          |t                    st          |          }||v r,|                    |           |                    |           W\|S r   )rp   r   r   _diagnostic_keyrF  r1  )r  seenr3  lstdr   s         r*   ry  ry  s  s    UUD "C   	 	Aa&& !!$$Cd{{HHSMMMJJqMMMM	 Jr,   r  Dict[str, Any]c                   |                      d          pi }|                     d          pi }|                     d          pi }|                      d          }|$t          |t                    st          |          }d                    t          |                      d          pd          t          |pd	          t          |                      d
          pd	          t          |                      d          pd	                                          |                     dd           d|                     dd           d|                     dd           d|                     dd           g          S )u  Content-equality key for a diagnostic.

    Matches the structural-equality used in claude-code's
    ``areDiagnosticsEqual`` — message + severity + source + code +
    range coords.  The range is reduced to a tuple to keep the key
    stable across dict orderings.
    r  r   rM  r  N severityr1   r&  sourcer  r:   r   r0   r;   -)r   r   r   joinstrip)r  rngr   rM  r  s        r*   r  r    sV    %%..
BCGGG"E
''%..
BC55==D
4 5 54yy;;j!!&Q''
OOh%2&&i  &B''--//yy##ppeiiQ&?&?pp#''&RSBTBTppWZW^W^_jlmWnWnpp	
  r,   )rG   r+   r5   r   re  rd  )r   r   r   r   )r-   r   r   r   )r6   r   r   r7   )r  rw  r   rw  )r  r  r   r   )0r  
__future__r   rw   loggingr$   pathlibr   typingr   r   r   r   r	   r
   r   urllib.parser   r   agent.lsp.protocolr   r   r   r   r   r   r   r   r   r   r   	getLoggerr   r   re  rd  rZ  rr  r   r  r  r+   r5   rE   rG   ry  r  __all__r*  r,   r*   <module>r     s  ( (R # " " " " "   				       F F F F F F F F F F F F F F F F F F ' ' ' ' ' ' ' '                          
	-	.	.    !   !  2 2 2 2 * * * *6 6 6 6(r# r# r# r# r# r# r# r#j      2  r,   