§
    ÷žyjžw  ã                  ó  — d Z ddlmZ ddlZddlZddlZddlZddlZddlm	Z	m
Z
mZmZmZmZ ddlmZ ddlmZmZ ddlmZmZmZ ddlmZmZ  ej        d	¦  «        Zd
ZdZ G d„ d¦  «        Z G d„ d¦  «        Z dd„Z!dgZ"dS )uò  Service-level orchestration for LSP clients.

The :class:`LSPService` is the bridge between the synchronous
file_operations layer and the async :class:`agent.lsp.client.LSPClient`.

Design choices:

- A **single asyncio event loop** runs in a background thread.  All
  client work happens on that loop.  Synchronous callers from
  ``tools/file_operations.py`` use :meth:`get_diagnostics_sync` to
  open + wait + drain in one blocking call.

- One client per ``(server_id, workspace_root)`` key.  Lazy spawn:
  the first request for a key spawns the client; subsequent requests
  re-use it.

- A **broken-set** records ``(server_id, workspace_root)`` pairs that
  failed to spawn or initialize.  These are never retried for the
  life of the service.  Mirrors OpenCode's design.

- A **delta baseline** map keeps "diagnostics-as-of-the-last-snapshot"
  per file.  ``snapshot_baseline()`` is called BEFORE a write; the
  next ``get_diagnostics_sync()`` returns only diagnostics that
  weren't in the baseline.  This is the lift from Claude Code's
  ``beforeFileEdited`` / ``getNewDiagnostics`` pattern, except wired
  to the local LSP layer instead of MCP IDE RPC.

The service is **off by default** â€” call :meth:`is_active` to check
whether it's actually doing anything.  When LSP is disabled in
config, when no git workspace can be detected, when all configured
servers are missing binaries and auto-install is off, ``is_active``
returns False and the file_operations layer falls through to the
in-process syntax check.
é    )ÚannotationsN)ÚAnyÚCallableÚDictÚListÚOptionalÚTuple)Úeventlog)ÚDIAGNOSTICS_DOCUMENT_WAITÚ	LSPClient)ÚServerContextÚfind_server_for_fileÚlanguage_id_for)Úclear_cacheÚresolve_workspace_for_filezagent.lsp.manageriX  é   c                  ó@   — e Zd ZdZdd„Zdd„Zdd„Zddœdd„Zdd„ZdS )Ú_BackgroundLoopuÃ   A daemon thread that owns one asyncio event loop.

    Provides :meth:`run` for synchronous callers â€” submits a coroutine
    to the loop and blocks until it finishes (or a timeout fires).
    ÚreturnÚNonec                óR   — d | _         d | _        t          j        ¦   «         | _        d S ©N)Ú_loopÚ_threadÚ	threadingÚEventÚ_ready©Úselfs    ú7/home/ragecks/.hermes/hermes-agent/agent/lsp/manager.pyÚ__init__z_BackgroundLoop.__init__H   s#   € Ø:>ˆŒ
Ø37ˆŒÝ”oÑ'Ô'ˆŒˆˆó    c                óÂ   — | j         �d S t          j        | j        dd¬¦  «        | _         | j                              ¦   «          | j                             d¬¦  «         d S )Nzhermes-lsp-loopT)ÚtargetÚnameÚdaemong      @©Útimeout)r   r   ÚThreadÚ_run_foreverÚstartr   Úwaitr   s    r    r+   z_BackgroundLoop.startM   si   € ØŒ<Ð#ØˆFÝ Ô'ØÔ$Ø"Øð
ñ 
ô 
ˆŒð
 	Œ×ÒÑÔÐØŒ×Ò ÐÑ%Ô%Ð%Ð%Ð%r"   c                ó^  — t          j        ¦   «         }|| _        t          j        |¦  «         | j                             ¦   «          	 |                     ¦   «          	 |                     ¦   «          d S # t          $ r Y d S w xY w# 	 |                     ¦   «          w # t          $ r Y w w xY wxY wr   )	ÚasyncioÚnew_event_loopr   Úset_event_loopr   ÚsetÚrun_foreverÚcloseÚ	Exception©r   Úloops     r    r*   z_BackgroundLoop._run_foreverX   sÁ   € ÝÔ%Ñ'Ô'ˆØˆŒ
ÝÔ˜tÑ$Ô$Ð$ØŒ�ŠÑÔÐð	Ø×ÒÑÔÐðØ—
’
‘”���øÝð ð ð Ø��ðøøøøðØ—
’
‘”��øÝð ð ð Ø�ðøøøøøøsB   Á	B ÁA4 Á4
BÂBÂB,ÂBÂB,Â
B)Â&B,Â(B)Â)B,Nr'   r(   úOptional[float]r   c               óB  — ddl m} | j        €7t          j        |¦  «        r|                     ¦   «          t          d¦  «        ‚ ||| j        ¦  «        }|€t          d¦  «        ‚	 |                     |¬¦  «        S # t          $ r | 	                    ¦   «          ‚ w xY w)zSubmit a coroutine to the loop and block until done.

        Returns the coroutine's result, or raises its exception.
        r   )Úsafe_schedule_threadsafeNzbackground loop not startedzbackground loop not runningr'   )
Úagent.async_utilsr9   r   r.   Úiscoroutiner3   ÚRuntimeErrorÚresultr4   Úcancel)r   Úcoror(   r9   Úfuts        r    Úrunz_BackgroundLoop.rune   s¹   € ð
 	?Ð>Ð>Ð>Ð>Ð>ØŒ:ÐÝÔ" 4Ñ(Ô(ð Ø—
’
‘”�ÝÐ<Ñ=Ô=Ð=Ø&Ð& t¨T¬ZÑ8Ô8ˆØˆ;ÝÐ<Ñ=Ô=Ð=ð	Ø—:’: g�:Ñ.Ô.Ð.øÝð 	ð 	ð 	Ø�JŠJ‰LŒLˆLØð	øøøs   Á(A> Á> Bc                óÔ   — | j         }|€d S 	 |                     |j        ¦  «         n# t          $ r Y nw xY w| j        �| j                             d¬¦  «         d | _         d | _        d S )Nç       @r'   )r   Úcall_soon_threadsafeÚstopr<   r   Újoinr5   s     r    rE   z_BackgroundLoop.stopx   s„   € ØŒzˆØˆ<ØˆFð	Ø×%Ò% d¤iÑ0Ô0Ð0Ð0øÝð 	ð 	ð 	ØˆDð	øøøàŒ<Ð#ØŒL×Ò cÐÑ*Ô*Ð*ØˆŒ
ØˆŒˆˆs   �( ¨
5´5©r   r   )r(   r7   r   r   )	Ú__name__Ú
__module__Ú__qualname__Ú__doc__r!   r+   r*   rA   rE   © r"   r    r   r   A   s‘   € € € € € ðð ð(ð (ð (ð (ð
	&ð 	&ð 	&ð 	&ðð ð ð ð 7;ð ð ð ð ð ð ð&ð ð ð ð ð r"   r   c                  óÊ   — e Zd ZdZddddedœd9d„Zed:d„¦   «         Zd;d„Zd<d„Z	d=d„Z
ddddœd>d%„Zd?d(„Zd@d)„ZdAd*„ZdBd,„ZdAd-„ZdCd/„Zd@d0„ZdDd3„Zd@d4„Zd@d5„Zd@d6„ZdEd8„ZdS )FÚ
LSPServicezÿThe process-wide LSP service.

    Created once via :meth:`create_from_config`; the
    :func:`agent.lsp.get_service` accessor manages the singleton.
    Most callers should use that accessor rather than constructing
    :class:`LSPService` directly.
    N)Úbinary_overridesÚenv_overridesÚinit_overridesÚdisabled_serversÚidle_timeoutÚenabledÚboolÚ	wait_modeÚstrÚwait_timeoutÚfloatÚinstall_strategyrO   úOptional[Dict[str, List[str]]]rP   ú#Optional[Dict[str, Dict[str, str]]]rQ   ú#Optional[Dict[str, Dict[str, Any]]]rR   úOptional[List[str]]rS   r   r   c       	        óD  — || _         |dv r|nd| _        || _        || _        |pi | _        |pi | _        |pi | _        t          |pg ¦  «        | _        |	| _	        t          ¦   «         | _        | j         r| j                             ¦   «          i | _        t          ¦   «         | _        i | _        i | _        t#          j        ¦   «         | _        d | _        i | _        | j         r;| j	        dk    r2| j                             |                      ¦   «         d¬¦  «         d S d S d S )N>   ÚfullÚdocumentra   r   rC   r'   )Ú_enabledÚ
_wait_modeÚ_wait_timeoutÚ_install_strategyÚ_binary_overridesÚ_env_overridesÚ_init_overridesr1   Ú_disabled_serversÚ_idle_timeoutr   r   r+   Ú_clientsÚ_brokenÚ	_spawningÚ
_last_usedr   ÚLockÚ_state_lockÚ_idle_reaper_taskÚ_delta_baselinerA   Ú_start_idle_reaper)
r   rT   rV   rX   rZ   rO   rP   rQ   rR   rS   s
             r    r!   zLSPService.__init__“   s;  € ð  ˆŒØ'0Ð4HÐ'HÐ'H˜)˜)ÈjˆŒØ)ˆÔØ!1ˆÔØ!1Ð!7°RˆÔØ+Ð1¨rˆÔØ-Ð3°ˆÔÝ!$Ð%5Ð%;¸Ñ!<Ô!<ˆÔØ)ˆÔå$Ñ&Ô&ˆŒ
ØŒ=ð 	ØŒJ×ÒÑÔÐð ;=ˆŒÝ™EœEˆŒØ@BˆŒØ8:ˆŒÝ$œ>Ñ+Ô+ˆÔØ9=ˆÔð ACˆÔàŒ=ð 	C˜TÔ/°!Ò3Ð3ØŒJ�NŠN˜4×2Ò2Ñ4Ô4¸cˆNÑBÔBÐBÐBÐBð	Cð 	CÐ3Ð3r"   úOptional['LSPService']c                óh  — 	 ddl m}  |¦   «         }n3# t          $ r&}t                               d|¦  «         Y d}~dS d}~ww xY wt          |t          ¦  «        r|                     d¦  «        pi ni }t          |t          ¦  «        si }t          |                     dd¦  «        ¦  «        }|                     dd	¦  «        }t          |                     d
t          ¦  «        ¦  «        }|                     dd¦  «        }	 t          |                     dt          ¦  «        ¦  «        }	n# t          t          f$ r
 t          }	Y nw xY wd|	cxk     rt          k     r
n nt          }	|                     d¦  «        pi }
g }i }i }i }t          |
t          ¦  «        �r|
                     ¦   «         D ]ð\  }}t          |t          ¦  «        sŒ|                     d¦  «        r|                     |¦  «         |                     d¦  «        }t          |t"          ¦  «        r|r|||<   |                     d¦  «        }t          |t          ¦  «        r!d„ |                     ¦   «         D ¦   «         ||<   |                     d¦  «        }t          |t          ¦  «        r|||<   Œñ | |||||||||	¬¦	  «	        S )zÆBuild a service from ``hermes_cli.config`` settings.

        Returns ``None`` if the config can't be loaded.  The service
        itself returns ``is_active()`` False when LSP is disabled.
        r   )Úload_config_readonlyzLSP config load failed: %sNÚlsprT   TrV   ra   rX   rZ   ÚautorS   ÚserversÚdisabledÚcommandÚenvc                ó4   — i | ]\  }}|t          |¦  «        “ŒS rL   )rW   )Ú.0ÚkÚvs      r    ú
<dictcomp>z1LSPService.create_from_config.<locals>.<dictcomp>ï   s$   € Ð*MÐ*MÐ*M¹¸¸A¨1­c°!©f¬fÐ*MÐ*MÐ*Mr"   Úinitialization_options)	rT   rV   rX   rZ   rO   rP   rQ   rR   rS   )Úhermes_cli.configrv   r4   ÚloggerÚdebugÚ
isinstanceÚdictÚgetrU   rY   r   ÚDEFAULT_IDLE_TIMEOUTÚ	TypeErrorÚ
ValueErrorÚMIN_IDLE_TIMEOUTÚitemsÚappendÚlist)Úclsrv   ÚcfgÚeÚlsp_cfgrT   rV   rX   rZ   rS   Úservers_cfgrz   rO   rP   rQ   r%   ÚsubÚcmdr|   Úinits                       r    Úcreate_from_configzLSPService.create_from_config¿   sï  € ð	Ø>Ð>Ð>Ð>Ð>Ð>Ø&Ð&Ñ(Ô(ˆCˆCøÝð 	ð 	ð 	Ý�LŠLÐ5°qÑ9Ô9Ð9Ø�4�4�4�4�4øøøøð	øøøõ -7°s½DÑ,AÔ,AÐI�3—7’7˜5‘>”>Ð' RøÀrˆÝ˜'¥4Ñ(Ô(ð 	ØˆGå�w—{’{ 9¨dÑ3Ô3Ñ4Ô4ˆØ—K’K ¨ZÑ8Ô8ˆ	Ý˜WŸ[š[¨Õ9RÑSÔSÑTÔTˆØ"Ÿ;š;Ð'9¸6ÑBÔBÐð	0Ý  §¢¨^Õ=QÑ!RÔ!RÑSÔSˆLˆLøÝ�:Ð&ð 	0ð 	0ð 	0Ý/ˆLˆLˆLð	0øøøàˆ|Ð.Ð.Ò.Ð.Õ.Ò.Ð.Ð.Ð.Ð.õ
 ,ˆLØ—k’k )Ñ,Ô,Ð2°ˆØˆØ13ÐØ35ˆØ46ˆÝ�k¥4Ñ(Ô(ñ 	0Ø(×.Ò.Ñ0Ô0ð 0ð 0‘	��cÝ! #¥tÑ,Ô,ð ØØ—7’7˜:Ñ&Ô&ð *Ø—O’O DÑ)Ô)Ð)Ø—g’g˜iÑ(Ô(�Ý˜c¥4Ñ(Ô(ð 1¨Sð 1Ø-0Ð$ TÑ*Ø—g’g˜e‘n”n�Ý˜c¥4Ñ(Ô(ð NØ*MÐ*MÀÇÂÁÄÐ*MÑ*MÔ*M�M $Ñ'Ø—w’wÐ7Ñ8Ô8�Ý˜d¥DÑ)Ô)ð 0Ø+/�N 4Ñ(øàˆsØØØ%Ø-Ø-Ø'Ø)Ø%Ø%ð

ñ 

ô 

ð 
	
s$   ‚ “
A�>¾AÄ(D, Ä,EÅEc                ó   — | j         S )z8Return True iff this service should be consulted at all.)rb   r   s    r    Ú	is_activezLSPService.is_active  s
   € àŒ}Ðr"   Ú	file_pathc                ó  — | j         sdS t          |¦  «        }|�|j        | j        v rdS t	          |¦  «        \  }}|r|sdS 	 |                     ||¦  «        p|}n# t          $ r |}Y nw xY w|j        |f| j        v rdS dS )u@  Return True iff LSP should run for this specific file.

        Gates on workspace detection (file or cwd inside a git worktree),
        on whether any registered server matches the extension, and
        on whether the (server_id, workspace_root) pair is in the
        broken-set from a previous spawn failure.

        Files in already-broken pairs return False so the file_operations
        layer skips the LSP path entirely â€” no spawn attempts, no
        timeout cost â€” until the service is restarted (``hermes lsp
        restart``) or the process exits.
        FNT)rb   r   Ú	server_idri   r   Úresolve_rootr4   rl   )r   r›   ÚsrvÚws_rootÚgated_inÚper_server_roots         r    Úenabled_forzLSPService.enabled_for  sÂ   € ð Œ}ð 	Ø�5Ý" 9Ñ-Ô-ˆØˆ;˜#œ-¨4Ô+AÐAÐAØ�5Ý6°yÑAÔAÑˆ�Øð 	˜Hð 	Ø�5ð
	&Ø!×.Ò.¨y¸'ÑBÔBÐMÀgˆOˆOøÝð 	&ð 	&ð 	&Ø%ˆOˆOˆOð	&øøøàŒM˜?Ð+¨t¬|Ð;Ð;Ø�5Øˆts   ÁA ÁA,Á+A,c                óø  — |                       |¦  «        sdS 	 t          d| j        dz   ¦  «        }| j                             |                      |¦  «        |¬¦  «        }|pg | j        t          j         	                    |¦  «        <   dS # t          $ rd}t                               d||¦  «         |                      ||¦  «         g | j        t          j         	                    |¦  «        <   Y d}~dS d}~ww xY w)uï  Snapshot current diagnostics for ``file_path`` as the delta baseline.

        Called BEFORE a write so the next ``get_diagnostics_sync()``
        can filter out pre-existing errors.  Best-effort â€” failures
        are silently swallowed so a flaky server can't break a write.

        Outer timeouts (e.g. server hangs during initialize) mark the
        (server_id, workspace_root) pair as broken so subsequent edits
        skip it instantly instead of re-paying the timeout cost.
        Ng       @g      @r'   z#baseline snapshot failed for %s: %s)r£   Úmaxrd   r   rA   Ú_snapshot_asyncrr   ÚosÚpathÚabspathr4   r„   r…   Ú_mark_broken_for_file)r   r›   ÚtÚdiagsr’   s        r    Úsnapshot_baselinezLSPService.snapshot_baseline)  s  € ð ×Ò 	Ñ*Ô*ð 	ØˆFð		Bõ �C˜Ô+¨cÑ1Ñ2Ô2ˆAØ”J—N’N 4×#7Ò#7¸	Ñ#BÔ#BÈA�NÑNÔNˆEØ?D¸{ÈˆDÔ ¥¤§¢°Ñ!;Ô!;Ñ<Ð<Ð<øÝð 	Bð 	Bð 	BÝ�LŠLÐ>À	È1ÑMÔMÐMØ×&Ò& y°!Ñ4Ô4Ð4Ø?AˆDÔ ¥¤§¢°Ñ!;Ô!;Ñ<Ð<Ð<Ð<Ð<Ð<Ð<øøøøð	Bøøøs   ™A0B Â
C9ÂAC4Ã4C9T)Údeltar(   Ú
line_shiftr®   r(   r7   r¯   ú(Optional[Callable[[int], Optional[int]]]úList[Dict[str, Any]]c               ó’  ‡— |                       |¦  «        sg S t          |¦  «        }|r|j        nd}	 |�|n	| j        dz   }| j                             |                      |¦  «        |¬¦  «        }nÁ# t          j        $ rS}	t          j
        ||¦  «         t                               d||	¦  «         |                      ||	¦  «         g cY d}	~	S d}	~	wt          $ rT}	t          j        |||	¦  «         t                               d||	¦  «         |                      ||	¦  «         g cY d}	~	S d}	~	ww xY w|€t          j
        ||d¬¦  «         g S t           j                             |¦  «        }
|r�| j                             |
¦  «        pg }|r.|�d	d
lm}  |||¦  «        }d„ |D ¦   «         Šˆfd„|D ¦   «         }	 | j                             |                      |¦  «        d¬¦  «        pg }n# t          $ r g }Y nw xY w|r
|| j        |
<   |r$t          j        ||t3          |¦  «        ¦  «         nt          j        ||¦  «         |S )u¸  Synchronously open ``file_path`` in the right server, wait for
        diagnostics, return them.

        If ``delta`` is True (default), the result is filtered against
        any baseline previously captured via :meth:`snapshot_baseline`.
        Diagnostics present in the baseline are removed so the caller
        only sees errors introduced by the current edit.

        When ``line_shift`` is provided, baseline diagnostics are
        remapped through it before the set-difference.  This handles
        the case where the edit deleted or inserted lines, causing
        pre-existing diagnostics below the edit point to surface at
        different line numbers in the post-edit snapshot â€” without
        the shift, they'd all look "introduced by this edit".  Pass
        a callable built by
        :func:`agent.lsp.range_shift.build_line_shift` (pre_text,
        post_text).  Omit when pre/post content isn't available;
        the unshifted comparison still catches diagnostics that
        didn't move.

        Returns an empty list when LSP is disabled, when no workspace
        can be detected, when no server matches, or when the server
        can't be spawned.  Never raises.
        ú?NrC   r'   z"LSP diagnostics timeout for %s: %sz'LSP diagnostics fetch failed for %s: %szfresh diagnostics)Úkindr   )Úshift_baselinec                ó,   — h | ]}t          |¦  «        ’ŒS rL   ©Ú	_diag_key)r~   Úds     r    ú	<setcomp>z2LSPService.get_diagnostics_sync.<locals>.<setcomp>Ž  s   € Ð7Ð7Ð7¨�	 !™œÐ7Ð7Ð7r"   c                ó6   •— g | ]}t          |¦  «        ‰v¯|‘ŒS rL   r·   )r~   r¹   Úseens     €r    ú
<listcomp>z3LSPService.get_diagnostics_sync.<locals>.<listcomp>�  s)   ø€ ÐFÐFÐF˜q­Y°q©\¬\ÀÐ-EÐ-E˜Ð-EÐ-EÐ-Er"   )r£   r   r�   rd   r   rA   Ú_open_and_wait_asyncr.   ÚTimeoutErrorr
   Úlog_timeoutr„   r…   rª   r4   Úlog_server_errorr§   r¨   r©   rr   rˆ   Úagent.lsp.range_shiftrµ   Ú_current_diags_asyncÚlog_diagnosticsÚlenÚ	log_clean)r   r›   r®   r(   r¯   rŸ   r�   r«   r¬   r’   Úabs_pathÚbaselinerµ   Úfreshr¼   s                 @r    Úget_diagnostics_synczLSPService.get_diagnostics_syncA  sÃ  ø€ ð@ ×Ò 	Ñ*Ô*ð 	ØˆIõ # 9Ñ-Ô-ˆØ%(Ð1�C”M�M¨cˆ	ð	Ø"Ð.��°DÔ4FÈÑ4LˆAØ”J—N’N 4×#<Ò#<¸YÑ#GÔ#GÐQR�NÑSÔSˆEˆEøÝÔ#ð 	ð 	ð 	ÝÔ  ¨IÑ6Ô6Ð6Ý�LŠLÐ=¸yÈ!ÑLÔLÐLØ×&Ò& y°!Ñ4Ô4Ð4ØˆIˆIˆIˆIˆIˆIøøøøÝð 	ð 	ð 	ÝÔ% i°¸AÑ>Ô>Ð>Ý�LŠLÐBÀIÈqÑQÔQÐQØ×&Ò& y°!Ñ4Ô4Ð4ØˆIˆIˆIˆIˆIˆIøøøøð		øøøð ˆ=õ Ô  ¨IÐ<OÐPÑPÔPÐPØˆIå”7—?’? 9Ñ-Ô-ˆØð 	7ØÔ+×/Ò/°Ñ9Ô9Ð?¸RˆHØð 
GØÐ)ð EÐDÐDÐDÐDÐDØ-˜~¨h¸
ÑCÔC�HØ7Ð7¨hÐ7Ñ7Ô7�ØFÐFÐFÐF EÐFÑFÔF�ðØœ
Ÿš t×'@Ò'@ÀÑ'KÔ'KÐUX˜ÑYÔYÐ_Ð]_��øÝð ð ð Ø���ðøøøàð 7Ø16�Ô$ XÑ.àð 	5ÝÔ$ Y°	½3¸u¹:¼:ÑFÔFÐFÐFåÔ˜y¨)Ñ4Ô4Ð4ØˆsC   ´=A2 Á2D0ÂACÃ	D0ÃD0ÃA	D+Ä%D0Ä+D0Æ<1G. Ç.G=Ç<G=ÚexcÚBaseExceptionc                óx  — t          |¦  «        }|€dS t          |¦  «        \  }}|r|sdS 	 |                     ||¦  «        p|}n# t          $ r |}Y nw xY w|j        |f}|| j        v }| j                             |¦  «         | j        5  | j         	                    |d¦  «        }	| j
         	                    |d¦  «         ddd¦  «         n# 1 swxY w Y   |	�@	 | j                             |	                     ¦   «         d¬¦  «         n# t          $ r Y nw xY w|st          j        |j        ||¦  «         dS dS )uþ  Mark the (server_id, workspace_root) pair as broken so subsequent
        edits skip it instantly instead of re-paying timeout cost.

        Called when the outer ``_loop.run`` timeout cancels an in-flight
        spawn/initialize that the inner ``_get_or_spawn`` task was still
        holding open.  Without this, every subsequent write would re-enter
        the spawn path and re-pay the full ``snapshot_baseline``
        timeout (8s) until the binary is fixed.

        Also kills any orphan client process that survived the cancelled
        future, and emits a single eventlog WARNING so the user knows
        which server gave up.

        ``exc`` is whatever exception the outer wrapper caught â€” used
        only for logging, never re-raised.
        Ng      ð?r'   )r   r   rž   r4   r�   rl   Úaddrp   rk   Úpoprn   r   rA   Úshutdownr
   Úlog_spawn_failed)
r   r›   rË   rŸ   r    Úgatedr¢   ÚkeyÚalready_brokenÚclients
             r    rª   z LSPService._mark_broken_for_file   sÊ  € õ" # 9Ñ-Ô-ˆØˆ;ØˆFÝ3°IÑ>Ô>‰ˆ�Øð 	˜Eð 	ØˆFð	&Ø!×.Ò.¨y¸'ÑBÔBÐMÀgˆOˆOøÝð 	&ð 	&ð 	&Ø%ˆOˆOˆOð	&øøøàŒ}˜oÐ.ˆØ ¤Ð,ˆØŒ×Ò˜ÑÔÐð Ôð 	+ð 	+Ø”]×&Ò& s¨DÑ1Ô1ˆFØŒO×Ò  TÑ*Ô*Ð*ð	+ð 	+ð 	+ñ 	+ô 	+ð 	+ð 	+ð 	+ð 	+ð 	+ð 	+øøøð 	+ð 	+ð 	+ð 	+ð Ððð ”
—’˜vŸšÑ0Ô0¸#�Ñ>Ô>Ð>Ð>øÝð ð ð Ø�ðøøøð ð 	KÝÔ% c¤m°_ÀcÑJÔJÐJÐJÐJð	Kð 	Ks5   ­A ÁAÁAÂ7CÃCÃCÃ.D Ä
DÄDc                ó*  — | j         sdS 	 | j                             |                      ¦   «         d¬¦  «         n2# t          $ r%}t
                               d|¦  «         Y d}~nd}~ww xY w| j                             ¦   «          t          ¦   «          dS )z3Tear down all clients and stop the background loop.Ng      $@r'   zLSP shutdown error: %s)	rb   r   rA   Ú_shutdown_asyncr4   r„   r…   rE   r   )r   r’   s     r    rÐ   zLSPService.shutdownÑ  sŸ   € àŒ}ð 	ØˆFð	6ØŒJ�NŠN˜4×/Ò/Ñ1Ô1¸4ˆNÑ@Ô@Ð@Ð@øÝð 	6ð 	6ð 	6Ý�LŠLÐ1°1Ñ5Ô5Ð5Ð5Ð5Ð5Ð5Ð5øøøøð	6øøøàŒ
�ŠÑÔÐÝ‰Œˆˆˆs   ‹.: º
A)ÁA$Á$A)c              ƒ  óÄ  K  — |                       |¦  «        ƒ d {V —†}|€g S 	 |                     |t          |¦  «        ¬¦  «        ƒ d {V —†}|                     ||| j        ¬¦  «        ƒ d {V —†}n4# t
          $ r'}t                               d|¦  «         g cY d }~S d }~ww xY w|                      |¦  «         |sg S t          | 
                    |d¬¦  «        ¦  «        S )N©Úlanguage_id)Úmodezsnapshot open/wait failed: %sT©Ú
fresh_only)Ú_get_or_spawnÚ	open_filer   Úwait_for_diagnosticsrc   r4   r„   r…   Ú_touchr�   Údiagnostics_for©r   r›   rÕ   ÚversionrÉ   r’   s         r    r¦   zLSPService._snapshot_asyncà  s$  è è € Ø×)Ò)¨)Ñ4Ô4Ð4Ð4Ð4Ð4Ð4Ð4ˆØˆ>ØˆIð	Ø"×,Ò,¨YÅOÐT]ÑD^ÔD^Ð,Ñ_Ô_Ð_Ð_Ð_Ð_Ð_Ð_ˆGØ ×5Ò5°iÀÈtÌÐ5Ñ_Ô_Ð_Ð_Ð_Ð_Ð_Ð_ˆEˆEøÝð 	ð 	ð 	Ý�LŠLÐ8¸!Ñ<Ô<Ð<ØˆIˆIˆIˆIˆIˆIøøøøð	øøøð 	�Š�FÑÔÐØð 	ð ˆIÝ�F×*Ò*¨9ÀÐ*ÑFÔFÑGÔGÐGs   £AA1 Á1
B"Á;BÂB"ÂB"úOptional[List[Dict[str, Any]]]c              ƒ  ó  K  — |                       |¦  «        ƒ d{V —†}|€dS 	 |                     |t          |¦  «        ¬¦  «        ƒ d{V —†}|                     |¦  «        ƒ d{V —† |                     ||| j        | j        ¬¦  «        ƒ d{V —†}n4# t          $ r'}t           	                    d||¦  «         Y d}~dS d}~ww xY w|  
                    |¦  «         |sdS t          |                     |d¬¦  «        ¦  «        S )uv  Open + wait for FRESH diagnostics.

        Returns the fresh diagnostic list, or ``None`` when the server
        never produced post-change data within the wait budget.  The
        distinction matters: ``[]`` means "server checked the new
        content, it's clean", ``None`` means "no verdict" â€” the caller
        must not substitute stale data for either.
        NrÙ   )rÛ   r(   zopen/wait failed for %s: %sTrÜ   )rÞ   rß   r   Ú	save_filerà   rc   rd   r4   r„   r…   rá   r�   râ   rã   s         r    r¾   zLSPService._open_and_wait_asyncò  sd  è è € ð ×)Ò)¨)Ñ4Ô4Ð4Ð4Ð4Ð4Ð4Ð4ˆØˆ>Ø�4ð	Ø"×,Ò,¨YÅOÐT]ÑD^ÔD^Ð,Ñ_Ô_Ð_Ð_Ð_Ð_Ð_Ð_ˆGØ×"Ò" 9Ñ-Ô-Ð-Ð-Ð-Ð-Ð-Ð-Ð-Ø ×5Ò5Ø˜7¨¬À$ÔBTð 6ñ ô ð ð ð ð ð ð ˆEˆEøõ ð 	ð 	ð 	Ý�LŠLÐ6¸	À1ÑEÔEÐEØ�4�4�4�4�4øøøøð	øøøð 	�Š�FÑÔÐØð 	Ø�4Ý�F×*Ò*¨9ÀÐ*ÑFÔFÑGÔGÐGs   £A.B Â
CÂB>Â>Cc              ƒ  ó(  K  — t          |¦  «        \  }}t          |¦  «        }|r|r|sg S | j        5  | j                             |j        |f¦  «        }d d d ¦  «         n# 1 swxY w Y   |€g S t          |                     |d¬¦  «        ¦  «        S )NTrÜ   )r   r   rp   rk   rˆ   r�   r�   râ   )r   r›   ÚwsrÒ   rŸ   rÕ   s         r    rÃ   zLSPService._current_diags_async  sä   è è € Ý.¨yÑ9Ô9‰	ˆˆEÝ" 9Ñ-Ô-ˆØð 	�uð 	 ð 	ØˆIØÔð 	<ð 	<Ø”]×&Ò&¨¬°rÐ':Ñ;Ô;ˆFð	<ð 	<ð 	<ñ 	<ô 	<ð 	<ð 	<ð 	<ð 	<ð 	<ð 	<øøøð 	<ð 	<ð 	<ð 	<àˆ>ØˆIÝ�F×*Ò*¨9ÀÐ*ÑFÔFÑGÔGÐGs   ³"A!Á!A%Á(A%úOptional[LSPClient]c           	   ƒ  óö  K  — t          |¦  «        }|€d S |j        | j        v rt          j        |j        |d¦  «         d S t          |¦  «        \  }}|r|st          j        |j        |¦  «         d S |                     ||¦  «        }|€t          j        |j        |d¦  «         d S |j        |f}|| j        v rd S | j	        5  | j
                             |¦  «        }|�J|j        rCt          j        ¦   «         | j        |<   t          j        |j        |¦  «         |cd d d ¦  «         S | j                             |¦  «        }d d d ¦  «         n# 1 swxY w Y   |�	 |ƒ d {V —†S # t"          $ r Y d S w xY wt%          j        ¦   «         }	|	                     ¦   «         }
| j	        5  |
| j        |<   d d d ¦  «         n# 1 swxY w Y   	 t+          || j        | j        | j        | j        ¬¦  «        }|                     ||¦  «        }|€Œt          j        |j        |j        ¦  «         | j                             |¦  «         |
                     d ¦  «         	 | j	        5  | j                             |d ¦  «         d d d ¦  «         d S # 1 swxY w Y   d S t?          |j        |j         |j!        |j"        |j#        |j$        |j%        p|j&        ¬¦  «        }	 | '                    ¦   «         ƒ d {V —† n�# t"          $ r�}t          j(        |j        ||¦  «         | j                             |¦  «         |
                     d ¦  «         Y d }~| j	        5  | j                             |d ¦  «         d d d ¦  «         d S # 1 swxY w Y   d S d }~ww xY w| j	        5  || j
        |<   t          j        ¦   «         | j        |<   d d d ¦  «         n# 1 swxY w Y   t          j        |j        |¦  «         |
                     |¦  «         || j	        5  | j                             |d ¦  «         d d d ¦  «         S # 1 swxY w Y   S # | j	        5  | j                             |d ¦  «         d d d ¦  «         w # 1 swxY w Y   w xY w)Nzdisabled in configz%exclude marker hit (server gated off))Úworkspace_rootrZ   rO   rP   rQ   )r�   rì   r{   r|   Úcwdr‚   Úseed_diagnostics_on_first_push))r   r�   ri   r
   Úlog_disabledr   Úlog_no_project_rootrž   rl   rp   rk   rˆ   Ú
is_runningÚtimern   Ú
log_activerm   r4   r.   Úget_running_loopÚcreate_futurer   re   rf   rg   rh   Úbuild_spawnÚlog_server_unavailablerÎ   Ú
set_resultrÏ   r   rì   r{   r|   rí   r‚   rî   Úseed_first_pushr+   rÑ   )r   r›   rŸ   r    rÒ   r¢   rÓ   rÕ   Úspawningr6   Úspawn_futureÚctxÚspecr’   s                 r    rÞ   zLSPService._get_or_spawn  s¶  è è € Ý" 9Ñ-Ô-ˆØˆ;Ø�4ØŒ=˜DÔ2Ð2Ð2ÝÔ! #¤-°Ð<PÑQÔQÐQØ�4Ý3°IÑ>Ô>‰ˆ�Øð 	˜Eð 	ÝÔ(¨¬¸	ÑBÔBÐBØ�4Ø×*Ò*¨9°gÑ>Ô>ˆØÐ"ÝÔ!Ø”˜yÐ*Qñô ð ð �4àŒ}˜oÐ.ˆØ�$”,ÐÐØ�4ØÔð 	/ð 	/Ø”]×&Ò& sÑ+Ô+ˆFØÐ! fÔ&7Ð!Ý'+¤y¡{¤{�” Ñ$ÝÔ# C¤M°?ÑCÔCÐCØð	/ð 	/ð 	/ð 	/ñ 	/ô 	/ð 	/ð 	/ð ”~×)Ò)¨#Ñ.Ô.ˆHð	/ð 	/ð 	/ñ 	/ô 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/øøøð 	/ð 	/ð 	/ð 	/ð ÐðØ%�~�~�~�~�~�~Ð%øÝð ð ð Ø�t�tðøøøõ Ô'Ñ)Ô)ˆØ'+×'9Ò'9Ñ';Ô';ˆØÔð 	/ð 	/Ø".ˆDŒN˜3Ñð	/ð 	/ð 	/ñ 	/ô 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/øøøð 	/ð 	/ð 	/ð 	/ð*	.ÝØ.Ø!%Ô!7Ø!%Ô!7Ø"Ô1Ø#Ô3ðñ ô ˆCð —?’? ?°CÑ8Ô8ˆDØˆ|õ
 Ô/°´¸s¼}ÑMÔMÐMØ”× Ò  Ñ%Ô%Ð%Ø×'Ò'¨Ñ-Ô-Ð-Øð0 Ô!ð .ð .Ø”×"Ò" 3¨Ñ-Ô-Ð-ð.ð .ð .ñ .ô .ð .ð .ð .ð .ð .ð .ð .øøøð .ð .ð .ð .ð .ð .õ/ Øœ-Ø#Ô2ØœØ”HØ”HØ'+Ô'BØ/3Ô/RÐ/iÐVYÔViðñ ô ˆFðØ—l’l‘n”nÐ$Ð$Ð$Ð$Ð$Ð$Ð$Ð$øÝð ð ð ÝÔ)¨#¬-¸È!ÑLÔLÐLØ”× Ò  Ñ%Ô%Ð%Ø×'Ò'¨Ñ-Ô-Ð-Ø�t�t�tð Ô!ð .ð .Ø”×"Ò" 3¨Ñ-Ô-Ð-ð.ð .ð .ñ .ô .ð .ð .ð .ð .ð .ð .ð .øøøð .ð .ð .ð .ð .ð .øøøøðøøøð
 Ô!ð 3ð 3Ø%+�”˜cÑ"Ý'+¤y¡{¤{�” Ñ$ð3ð 3ð 3ñ 3ô 3ð 3ð 3ð 3ð 3ð 3ð 3øøøð 3ð 3ð 3ð 3õ Ô ¤¨Ñ?Ô?Ð?Ø×#Ò# FÑ+Ô+Ð+ØàÔ!ð .ð .Ø”×"Ò" 3¨Ñ-Ô-Ð-ð.ð .ð .ñ .ô .ð .ð .ð .ð .ð .ð .øøøð .ð .ð .ð .ð .ø�Ô!ð .ð .Ø”×"Ò" 3¨Ñ-Ô-Ð-ð.ð .ð .ñ .ô .ð .ð .ð .ð .ð .ð .øøøð .ð .ð .ð .ð .øøøsú   ÃAEÄ*EÅEÅEÅE& Å&
E4Å3E4Æ%F<Æ<G ÇG ÇBP< ÉJÊJÊJÊA P< ËK0 Ë/P< Ë0
N
Ë:A
NÍP< ÍM8Í8M<Í?M<ÎN
Î

P< Î&OÎ:P< ÏO
Ï
P< ÏO
Ï3P< ÐP0Ð0P4	Ð7P4	Ð<Q8ÑQ,Ñ Q8Ñ,Q0Ñ0Q8Ñ3Q0Ñ4Q8c              ƒ  ó`   K  — t          j        |                      ¦   «         ¦  «        | _        d S r   )r.   Úcreate_taskÚ_idle_reaper_looprq   r   s    r    rs   zLSPService._start_idle_reaperj  s+   è è € Ý!(Ô!4°T×5KÒ5KÑ5MÔ5MÑ!NÔ!NˆÔÐÐr"   rÕ   r   c                óª   — |j         |j        f}| j        5  || j        v rt	          j        ¦   «         | j        |<   ddd¦  «         dS # 1 swxY w Y   dS )aa  Refresh the last-used timestamp for a client we just used.

        Guarded on membership so a reaped-mid-operation client can't
        resurrect an orphan ``_last_used`` entry after the reaper popped
        the key.  All writers and the reaper run on the background loop
        thread; the lock keeps this consistent with the reader anyway.
        N)r�   rì   rp   rk   rò   rn   )r   rÕ   rÓ   s      r    rá   zLSPService._touchm  s    € ð Ô Ô!6Ð7ˆØÔð 	3ð 	3Ø�d”mÐ#Ð#Ý'+¤y¡{¤{�” Ñ$ð	3ð 	3ð 	3ñ 	3ô 	3ð 	3ð 	3ð 	3ð 	3ð 	3ð 	3ð 	3øøøð 	3ð 	3ð 	3ð 	3ð 	3ð 	3s   –%AÁAÁAc              ƒ  ó"  K  — t          d| j        ¦  «        }	 t          j        |¦  «        ƒ d {V —† 	 |                      ¦   «         ƒ d {V —† nA# t          j        $ r ‚ t          $ r%}t                               d|¦  «         Y d }~nd }~ww xY wŒx)Ng      N@TzLSP idle reaper sweep error: %s)	Úminrj   r.   ÚsleepÚ_reap_idle_onceÚCancelledErrorr4   r„   r…   )r   Úintervalr’   s      r    r   zLSPService._idle_reaper_loopz  sÔ   è è € Ý�t˜TÔ/Ñ0Ô0ˆð
	CÝ”- Ñ)Ô)Ð)Ð)Ð)Ð)Ð)Ð)Ð)ðCØ×*Ò*Ñ,Ô,Ð,Ð,Ð,Ð,Ð,Ð,Ð,Ð,øÝÔ)ð ð ð ØÝð Cð Cð Cõ —’Ð>ÀÑBÔBÐBÐBÐBÐBÐBÐBøøøøð	Cøøøð
	Cs   ´A ÁBÁ(BÂBc              ƒ  óš  ‡ ‡K  — t          j         ¦   «         ‰ j        z
  Š‰ j        5  ˆˆ fd„‰ j        D ¦   «         }ˆ fd„|D ¦   «         }|D ]}‰ j                             |d ¦  «         Œ	 d d d ¦  «         n# 1 swxY w Y   |rGt          j        d„ |D ¦   «         ‰ j        ¦  «         t          j	        d„ |D ¦   «         ddiŽƒ d {V —† d S d S )Nc                óR   •— g | ]#}‰j                              |d ¦  «        ‰k     ¯!|‘Œ$S )r   )rn   rˆ   )r~   rÓ   Úcutoffr   s     €€r    r½   z.LSPService._reap_idle_once.<locals>.<listcomp>‹  s@   ø€ ð ð ð àØ”?×&Ò& s¨AÑ.Ô.°Ò7Ð7ð à7Ð7Ð7r"   c                óD   •— g | ]}‰j                              |¦  «        ‘ŒS rL   )rk   rÏ   )r~   rÓ   r   s     €r    r½   z.LSPService._reap_idle_once.<locals>.<listcomp>�  s)   ø€ ÐCÐCÐC°#�t”}×(Ò(¨Ñ-Ô-ÐCÐCÐCr"   c                ó*   — g | ]}|j         |j        f‘ŒS rL   )r�   rì   ©r~   Úcs     r    r½   z.LSPService._reap_idle_once.<locals>.<listcomp>•  s"   € ÐBÐBÐB°Q�!”+˜qÔ/Ð0ÐBÐBÐBr"   c              3  ó>   K  — | ]}|                      ¦   «         V — Œd S r   ©rÐ   )r~   rÕ   s     r    ú	<genexpr>z-LSPService._reap_idle_once.<locals>.<genexpr>™  s,   è è € Ð:Ð:¨�&—/’/Ñ#Ô#Ð:Ð:Ð:Ð:Ð:Ð:r"   Úreturn_exceptionsT)
rò   rj   rp   rk   rn   rÏ   r
   Ú
log_reapedr.   Úgather)r   Ú	idle_keysÚclientsrÓ   r
  s   `   @r    r  zLSPService._reap_idle_onceˆ  sŠ  øøè è € Ý”‘”˜tÔ1Ñ1ˆØÔð 	/ð 	/ðð ð ð ð àœ=ðñ ô ˆIð
 DÐCÐCÐC¸ÐCÑCÔCˆGØ ð /ð /�Ø”×#Ò# C¨Ñ.Ô.Ð.Ð.ð/ð	/ð 	/ð 	/ñ 	/ô 	/ð 	/ð 	/ð 	/ð 	/ð 	/ð 	/øøøð 	/ð 	/ð 	/ð 	/ð ð 	ÝÔØBÐB¸'ÐBÑBÔBØÔ"ñô ð õ ”.Ø:Ð:°'Ð:Ñ:Ô:ðà"&ðð ð ð ð ð ð ð ð ð ð ð	ð 	s   §AA7Á7A;Á>A;c              ƒ  óì  K  — | j         }d | _         |�0|                     ¦   «          t          j        |d¬¦  «        ƒ d {V —† | j        5  t          | j                             ¦   «         ¦  «        }| j                             ¦   «          | j	                             ¦   «          | j
                             ¦   «          d d d ¦  «         n# 1 swxY w Y   t          j        d„ |D ¦   «         ddiŽƒ d {V —† d S )NT)r  c              3  ó>   K  — | ]}|                      ¦   «         V — Œd S r   r  r  s     r    r  z-LSPService._shutdown_async.<locals>.<genexpr>©  s*   è è € Ð,Ð,˜qˆa�jŠj‰lŒlÐ,Ð,Ð,Ð,Ð,Ð,r"   r  )rq   r>   r.   r  rp   r�   rk   ÚvaluesÚclearrl   rn   )r   Úreaperr  s      r    r×   zLSPService._shutdown_async�  s\  è è € ØÔ'ˆØ!%ˆÔØÐØ�MŠM‰OŒOˆOÝ”. ¸4Ð@Ñ@Ô@Ð@Ð@Ð@Ð@Ð@Ð@Ð@ØÔð 	$ð 	$Ý˜4œ=×/Ò/Ñ1Ô1Ñ2Ô2ˆGØŒM×ÒÑ!Ô!Ð!ØŒL×ÒÑ Ô Ð ØŒO×!Ò!Ñ#Ô#Ð#ð		$ð 	$ð 	$ñ 	$ô 	$ð 	$ð 	$ð 	$ð 	$ð 	$ð 	$øøøð 	$ð 	$ð 	$ð 	$õ
 ŒnØ,Ð, GÐ,Ñ,Ô,ð
à"ð
ð 
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
s   Á
A2CÃCÃCúDict[str, Any]c           	     ó  — | j         5  d„ | j                             ¦   «         D ¦   «         }t          | j        ¦  «        }ddd¦  «         n# 1 swxY w Y   | j        | j        | j        | j        ||t          | j
        ¦  «        dœS )z<Return a snapshot of the service for the CLI status command.c                óN   — g | ]"\  }}|d          |d         |j         |j        dœ‘Œ#S )r   é   )r�   rì   ÚstateÚrunning)r   rñ   )r~   r   r  s      r    r½   z)LSPService.get_status.<locals>.<listcomp>´  sM   € ð ð ð ñ �A�qð "# 1¤Ø&'¨¤dØœWØ œ|ð	ð ðð ð r"   N)rT   rV   rX   rZ   r  ÚbrokenrR   )rp   rk   r�   r�   rl   rb   rc   rd   re   Úsortedri   )r   r  r"  s      r    Ú
get_statuszLSPService.get_status±  sÚ   € àÔð 
	(ð 
	(ðð ð !œM×/Ò/Ñ1Ô1ðñ ô ˆGõ ˜$œ,Ñ'Ô'ˆFð
	(ð 
	(ð 
	(ñ 
	(ô 
	(ð 
	(ð 
	(ð 
	(ð 
	(ð 
	(ð 
	(øøøð 
	(ð 
	(ð 
	(ð 
	(ð ”}ØœØ Ô.Ø $Ô 6ØØÝ & tÔ'=Ñ >Ô >ð
ð 
ð 	
s   ˆ8AÁAÁA)rT   rU   rV   rW   rX   rY   rZ   rW   rO   r[   rP   r\   rQ   r]   rR   r^   rS   rY   r   r   )r   rt   )r   rU   )r›   rW   r   rU   )r›   rW   r   r   )
r›   rW   r®   rU   r(   r7   r¯   r°   r   r±   )r›   rW   rË   rÌ   r   r   rG   )r›   rW   r   r±   )r›   rW   r   rå   )r›   rW   r   rê   )rÕ   r   r   r   )r   r  )rH   rI   rJ   rK   r‰   r!   Úclassmethodr˜   rš   r£   r­   rÊ   rª   rÐ   r¦   r¾   rÃ   rÞ   rs   rá   r   r  r×   r$  rL   r"   r    rN   rN   †   sõ  € € € € € ðð ð& <@Ø=AØ>BØ04Ø2ð*Cð *Cð *Cð *Cð *Cð *CðX ð>
ð >
ð >
ñ „[ð>
ðHð ð ð ðð ð ð ðBBð Bð Bð Bð8 Ø#'Ø?Cð]ð ]ð ]ð ]ð ]ð ]ð~/Kð /Kð /Kð /Kðb	ð 	ð 	ð 	ðHð Hð Hð Hð$Hð Hð Hð Hð4	Hð 	Hð 	Hð 	HðQ.ð Q.ð Q.ð Q.ðfOð Oð Oð Oð3ð 3ð 3ð 3ðCð Cð Cð Cðð ð ð ð*
ð 
ð 
ð 
ð(
ð 
ð 
ð 
ð 
ð 
r"   rN   r¹   r  r   rW   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 )up  Content equality key used for cross-edit delta filtering.

    Includes the diagnostic's position range â€” when used together
    with :func:`agent.lsp.range_shift.shift_baseline`, the baseline
    is line-shifted into post-edit coordinates BEFORE this key is
    computed, so identical-but-shifted diagnostics hash equal.  Two
    genuinely distinct diagnostics at different lines (e.g. the same
    error class introduced at a second site) hash differently and
    are surfaced as new.

    Mirrors :func:`agent.lsp.client._diagnostic_key`; intentionally
    identical so the two layers agree on diagnostic identity.
    Úranger+   ÚendÚcodeNú Úseverityr  Ú ÚsourceÚmessageÚliner   ú:Ú	characterú-)rˆ   r†   rW   rF   Ústrip)r¹   Úrngr+   r(  r)  s        r    r¸   r¸   É  sV  € ð �%Š%�‰.Œ.Ð
˜B€CØ�GŠG�GÑÔÐ" €EØ
�'Š'�%‰.Œ.Ð
˜B€CØ�5Š5�‰=Œ=€DØÐ¥
¨4µÑ 5Ô 5ÐÝ�4‰yŒyˆØ�;Š;å�—’�jÑ!Ô!Ð& QÑ'Ô'Ý��
˜‰OŒOÝ�—’�h‘”Ð% 2Ñ&Ô&Ý�—’�iÑ Ô Ð& BÑ'Ô'×-Ò-Ñ/Ô/Ø�yŠy˜ Ñ#Ô#ÐpÐp e§i¢i°¸QÑ&?Ô&?ÐpÐpÀ#Ç'Â'È&ÐRSÑBTÔBTÐpÐpÐWZ×W^ÒW^Ð_jÐlmÑWnÔWnÐpÐpð	
ñô ð r"   )r¹   r  r   rW   )#rK   Ú
__future__r   r.   Úloggingr§   r   rò   Útypingr   r   r   r   r   r	   Ú	agent.lspr
   Úagent.lsp.clientr   r   Úagent.lsp.serversr   r   r   Úagent.lsp.workspacer   r   Ú	getLoggerr„   r‰   rŒ   r   rN   r¸   Ú__all__rL   r"   r    ú<module>r>     s¼  ðð!ð !ðD #Ð "Ð "Ð "Ð "Ð "à €€€Ø €€€Ø 	€	€	€	Ø Ð Ð Ð Ø €€€Ø =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =à Ð Ð Ð Ð Ð ðð ð ð ð ð ð ð ðð ð ð ð ð ð ð ð ð ð
ð ð ð ð ð ð ð ð
 
ˆÔ	Ð.Ñ	/Ô	/€àÐ ØÐ ðBð Bð Bð Bð Bñ Bô Bð BðJ@	
ð @	
ð @	
ð @	
ð @	
ñ @	
ô @	
ð @	
ðFð ð ð ð> ˆ.€€€r"   