;;; lsp-metta.el --- LSP client for Metta -*- lexical-binding: t; -*-
;; Author: Douglas R. Miles
;; Keywords: lsp, metta
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU Lesser General Public License as published by
;; the Free Software Foundation; either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU Lesser General Public License for more details.
;; You should have received a copy of the GNU Lesser General Public License
;; along with this program. If not, see .
;;; Commentary:
;;
;; This Emacs package provides a Metta language major mode and integrates
;; an LSP client for enhanced language features. To use this package,
;; simply place it in your Emacs directory, typically ~/.emacs.d/,
;; and add the following line to your ~/.emacs or init.el file:
;;
;; (load "path/to/lsp-metta.el")
;;
;; Ensure you adjust "path/to/" to the actual path where you saved this file.
;; This will set up the major mode and LSP client whenever you open a Metta
;; file with the .metta extension.
;;
;; Example using use-package:
;;
;; (use-package metta-mode
;; :load-path "path/to/lsp-metta" ; Adjust the path as needed
;; :config
;; (setq some-metta-config-var 'value)
;; (add-hook 'metta-mode-hook #'lsp))
;;
;;; Code:
;; Ensure lsp-mode is loaded for LSP client configuration
(require 'lsp-mode)
;; Define the mode's keymap
(defvar metta-mode-map
(let ((map (make-sparse-keymap)))
;; Define keybindings here if needed
map)
"Keymap for `metta-mode'.")
;; Define syntax table
(defvar metta-mode-syntax-table
(let ((st (make-syntax-table)))
;; MeTTa comments start with ';'
(modify-syntax-entry ?\; "<" st)
(modify-syntax-entry ?\n ">" st)
st)
"Syntax table for `metta-mode'.")
;; Define the list of keywords
(defvar metta-font-lock-keywords
(let* (
(x-keywords '("=" "if" "superpose" "!" "let" "let*" "car-atom" "cdr-atom"))
(x-constants '("True" "False" "Type"))
;; Regex patterns to match any keyword starting with '@' or '&'
(at-constants "@\\w+")
(amp-constants "&\\w+")
(x-keywords-regexp (regexp-opt x-keywords 'words))
(x-constants-regexp (regexp-opt x-constants 'words)))
`((,x-keywords-regexp . font-lock-keyword-face)
(,x-constants-regexp . font-lock-constant-face)
(,at-constants . font-lock-constant-face) ;; Apply constant face to @ prefixed words
(,amp-constants . font-lock-constant-face) ;; Apply constant face to & prefixed words
)))
(define-derived-mode metta-mode prog-mode "Metta"
"A major mode for editing Metta language code."
(set-syntax-table metta-mode-syntax-table)
(setq font-lock-defaults '(metta-font-lock-keywords)))
;; Register .metta files with metta-mode
(add-to-list 'auto-mode-alist '("\\.metta\\'" . metta-mode))
;; LSP client configuration for Metta language
(with-eval-after-load 'lsp-mode
;; Register the language ID
(add-to-list 'lsp-language-id-configuration '(metta-mode . "metta"))
(defgroup lsp-metta nil
"LSP support for Metta."
:group 'lsp-mode
:link '(url-link "https://github.com/trueagi-io/metta-wam"))
(lsp-register-client
(make-lsp-client :new-connection
(lsp-stdio-connection (list "swipl"
"-g" "use_module(library(lsp_server_metta))."
"-g" "lsp_server_metta:main"
"-t" "halt"
"--" "stdio"))
:activation-fn (lsp-activate-on "metta")
;; :major-modes '(metta-mode)
:priority -1
:multi-root t
:server-id 'metta-lsp))
(add-hook 'metta-mode-hook #'lsp))
(require 'ffap)
(defun my-find-file-at-line (path)
"Open the specified file at a given line number or range extracted from PATH."
(let ((components (split-string path "#L")))
(if (> (length components) 1)
(let* ((file-range (split-string (cadr components) "-"))
(file (url-unhex-string (car components)))
(start-line (string-to-number (car file-range)))
(end-line (if (> (length file-range) 1) (string-to-number (cadr file-range)) start-line)))
(if (file-exists-p file)
(progn
(find-file file)
(goto-line start-line)
(when (> end-line start-line)
(push-mark (point) nil t)
(goto-line (1+ end-line)) ;; Move slightly past to include the end line fully
(end-of-line))
(message "Opened %s from line %d to %d" file start-line end-line))
(error "File does not exist: %s" file)))
(find-file (url-unhex-string path)))))
(with-eval-after-load 'ffap
(ffap-url-unwrap-remote "file")
(setq ffap-url-fetcher 'my-find-file-at-line))
(defun my-md-follow-link ()
"Custom follow link function to handle file links with line numbers in Markdown."
(interactive)
(let ((link (thing-at-point 'url)))
(if (and link (string-match "^file://" link))
(my-find-file-at-line link)
(markdown-follow-link-at-point))))
(with-eval-after-load 'markdown-mode
(define-key markdown-mode-map (kbd "") 'my-md-follow-link))
;; Provide the Metta mode feature
(provide 'metta-mode)
;;; lsp-metta.el ends here