<commit_before>(in-package :lem-language-client)

(defparameter *root-path* (probe-file "."))
(defparameter *log-stream* *error-output*)

(defparameter |TextDocumentSaveReason.Manual| 1)
(defparameter |TextDocumentSaveReason.AfterDelay| 2)
(defparameter |TextDocumentSaveReason.FocusOut| 3)

(defvar *workspaces* '())
(defvar *response-methods* '())

(defstruct workspace
  connection
  server-capabilities
  text-document-sync
  root
  language-id
  (file-version-table (make-hash-table)))

(defstruct text-document-sync
  open-close
  change
  will-save
  will-save-wait-until
  save)

(lem:define-minor-mode language-client-mode
    (:name "Language Client")
  (setf (lem:variable-value 'lem.language-mode:completion-function) 'completion)
  ;(uiop:run-program "go-langserver -mode tcp -gocodecompletion &")
  (let ((buffer (lem:current-buffer)))
    (start (lem:buffer-major-mode buffer) buffer)))

(defun get-language-id (language)
  (ecase language
    (lem-js-mode:js-mode "javascript")
    (lem-go-mode:go-mode "go")))

(defmacro define-response-method (name (&rest vars) &body body)
  (alexandria:with-gensyms (params)
    `(pushnew
      (defun ,name (,params)
        (let ,(mapcar (lambda (var)
                        `(,var (gethash ,(string var) ,params)))
                      vars)
          ,@body))
      *response-methods*)))

(defun pathname-to-uri (pathname)
  (format nil "file://~A" pathname))

(defun {} (&rest plist)
  (alexandria:plist-hash-table plist :test 'equal))

(defun -> (table &rest keys)
  (loop :for k* :on keys
        :for k := (first k*)
        :do (if (rest k*)
                (progn
                  (setf table (gethash k table))
                  (unless (hash-table-p table)
                    (return nil)))
                (setf table (gethash k table)))
        :finally (return table)))

(defun merge-table (parent child)
  (maphash (lambda (key value)
             (setf (gethash key child) value))
           parent)
  child)

(defun find-workspace (root-path)
  (find root-path *workspaces*
        :test #'equal
        :key #'workspace-root))

(defun buffer-language-id (buffer)
  (lem:mode-name (lem:buffer-major-mode buffer)))

(defun buffer-workspace (buffer)
  (lem:buffer-value buffer 'workspace))

(defun (setf buffer-workspace) (workspace buffer)
  (setf (lem:buffer-value buffer 'workspace) workspace))

(defun buffer-file-version (buffer)
  (gethash buffer (workspace-file-version-table (buffer-workspace buffer)) 0))

(defun (setf buffer-file-version) (value buffer)
  (setf (gethash buffer (workspace-file-version-table (buffer-workspace buffer))) value))

(defun buffer-text (buffer)
  (lem:points-to-string (lem:buffer-start-point buffer)
                        (lem:buffer-end-point buffer)))

(defun buffer-uri (buffer)
  (let ((filename (lem:buffer-filename buffer)))
    (pathname-to-uri (if (alexandria:starts-with-subseq "~/" filename)
                         (merge-pathnames (subseq filename 2) (user-homedir-pathname))
                         filename))))

(defun lsp-position (point)
  ({} "line" (1- (lem:line-number-at-point point))
      "character" (lem:point-charpos point)))

(defun lsp-range (start end)
  (when (lem:point< end start)
    (rotatef start end))
  ({} "start" (lsp-position start)
      "end" (lsp-position end)))

(defun lsp-location (start end)
  (let ((buffer (lem:point-buffer start)))
    ({} "uri" (buffer-uri buffer)
        "range" (lsp-range start end))))

(defun move-to-lsp-position (point position)
  (let-hash (|line| |character|) position
    (lem:move-to-line point (1+ |line|))
    (lem:line-offset point 0 |character|)
    point))

(defun decode-lsp-text-edit (buffer text-edit)
  (let-hash (|range| |newText|) text-edit
    (let-hash (|start| |end|) |range|
      (lem:with-point ((start-point (lem:buffer-point buffer))
                       (end-point (lem:buffer-point buffer)))
        (move-to-lsp-position start-point |start|)
        (move-to-lsp-position (lem:move-point end-point start-point) |end|)
        (values start-point end-point |newText|)))))

(defun text-document-identifier (buffer)
  ({} "uri" (pathname-to-uri buffer)))

(defun text-document-item (buffer)
  ({} "uri" (buffer-uri buffer)
      "languageId" (workspace-language-id (buffer-workspace buffer))
      "version" (buffer-file-version buffer)
      "text" (buffer-text buffer)))

(defun versioned-text-document-identifier (buffer)
  (let ((text-document-identifier (text-document-identifier buffer))
        (version (buffer-file-version buffer)))
    (merge-table text-document-identifier
                 ({} "version" version))))

(defun text-document-position-params (point)
  ({} "textDocument" (text-document-identifier (lem:point-buffer point))
      "position" (lsp-position point)))

(defun workspace-client-capabilities ()
  ({} "applyEdit" 'yason:false
      "workspaceEdit" ({} "documentChanges" 'yason:false)
      "didChangeConfiguration" ({} "dynamicRegistration" 'yason:false)<commit_msg>fix uri<commit_after>(in-package :lem-language-client)

(defparameter *root-path* (probe-file "."))
(defparameter *log-stream* *error-output*)

(defparameter |TextDocumentSaveReason.Manual| 1)
(defparameter |TextDocumentSaveReason.AfterDelay| 2)
(defparameter |TextDocumentSaveReason.FocusOut| 3)

(defvar *workspaces* '())
(defvar *response-methods* '())

(defstruct workspace
  connection
  server-capabilities
  text-document-sync
  root
  language-id
  (file-version-table (make-hash-table)))

(defstruct text-document-sync
  open-close
  change
  will-save
  will-save-wait-until
  save)

(lem:define-minor-mode language-client-mode
    (:name "Language Client")
  (setf (lem:variable-value 'lem.language-mode:completion-function) 'completion)
  ;(uiop:run-program "go-langserver -mode tcp -gocodecompletion &")
  (let ((buffer (lem:current-buffer)))
    (start (lem:buffer-major-mode buffer) buffer)))

(defun get-language-id (language)
  (ecase language
    (lem-js-mode:js-mode "javascript")
    (lem-go-mode:go-mode "go")))

(defmacro define-response-method (name (&rest vars) &body body)
  (alexandria:with-gensyms (params)
    `(pushnew
      (defun ,name (,params)
        (let ,(mapcar (lambda (var)
                        `(,var (gethash ,(string var) ,params)))
                      vars)
          ,@body))
      *response-methods*)))

(defun pathname-to-uri (pathname)
  (let ((filename (namestring pathname)))
    (format nil "file://~A"
            (if (alexandria:starts-with-subseq "~/" filename)
                (merge-pathnames (subseq filename 2) (user-homedir-pathname))
                filename))))

(defun {} (&rest plist)
  (alexandria:plist-hash-table plist :test 'equal))

(defun -> (table &rest keys)
  (loop :for k* :on keys
        :for k := (first k*)
        :do (if (rest k*)
                (progn
                  (setf table (gethash k table))
                  (unless (hash-table-p table)
                    (return nil)))
                (setf table (gethash k table)))
        :finally (return table)))

(defun merge-table (parent child)
  (maphash (lambda (key value)
             (setf (gethash key child) value))
           parent)
  child)

(defun find-workspace (root-path)
  (find root-path *workspaces*
        :test #'equal
        :key #'workspace-root))

(defun buffer-language-id (buffer)
  (lem:mode-name (lem:buffer-major-mode buffer)))

(defun buffer-workspace (buffer)
  (lem:buffer-value buffer 'workspace))

(defun (setf buffer-workspace) (workspace buffer)
  (setf (lem:buffer-value buffer 'workspace) workspace))

(defun buffer-file-version (buffer)
  (gethash buffer (workspace-file-version-table (buffer-workspace buffer)) 0))

(defun (setf buffer-file-version) (value buffer)
  (setf (gethash buffer (workspace-file-version-table (buffer-workspace buffer))) value))

(defun buffer-text (buffer)
  (lem:points-to-string (lem:buffer-start-point buffer)
                        (lem:buffer-end-point buffer)))

(defun buffer-uri (buffer)
  (pathname-to-uri (lem:buffer-filename buffer)))

(defun lsp-position (point)
  ({} "line" (1- (lem:line-number-at-point point))
      "character" (lem:point-charpos point)))

(defun lsp-range (start end)
  (when (lem:point< end start)
    (rotatef start end))
  ({} "start" (lsp-position start)
      "end" (lsp-position end)))

(defun lsp-location (start end)
  (let ((buffer (lem:point-buffer start)))
    ({} "uri" (buffer-uri buffer)
        "range" (lsp-range start end))))

(defun move-to-lsp-position (point position)
  (let-hash (|line| |character|) position
    (lem:move-to-line point (1+ |line|))
    (lem:line-offset point 0 |character|)
    point))

(defun decode-lsp-text-edit (buffer text-edit)
  (let-hash (|range| |newText|) text-edit
    (let-hash (|start| |end|) |range|
      (lem:with-point ((start-point (lem:buffer-point buffer))
                       (end-point (lem:buffer-point buffer)))
        (move-to-lsp-position start-point |start|)
        (move-to-lsp-position (lem:move-point end-point start-point) |end|)
        (values start-point end-point |newText|)))))

(defun text-document-identifier (buffer)
  ({} "uri" (buffer-uri buffer)))

(defun text-document-item (buffer)
  ({} "uri" (buffer-uri buffer)
      "languageId" (workspace-language-id (buffer-workspace buffer))
      "version" (buffer-file-version buffer)
      "text" (buffer-text buffer)))

(defun versioned-text-document-identifier (buffer)
  (let ((text-document-identifier (text-document-identifier buffer))
        (version (buffer-file-version buffer)))
    (merge-table text-document-identifier
                 ({} "version" version))))

(defun text-document-position-params (point)
  ({} "textDocument" (text-document-identifier (lem:point-buffer point))
      "position" (lsp-position point)))

(defun workspace-client-capabilities ()
  ({} "applyEdit" 'yason:false
      "workspaceEdit" ({} "documentChanges" 'yason:false)
      "didChangeConfiguration" ({} "dynamicRegistration" 'yason:false)
{'fraction_non_alphanumeric': 0.1559880239520958, 'fraction_numerical': 0.0015968063872255488, 'mean_word_length': 3.249787955894826, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5701580', 'n_tokens_mistral': 3359, 'n_tokens_neox': 3305, 'n_words': 763}