Programming Languages

Table of Contents

Language-specific configurations and development tools.

1. Overview

This section is organized by programming language for better maintainability. Each language has its own module with dedicated tooling and workflows.

2. Module Organization

The programming configuration is split into focused modules:

Module Description File
Loader Orchestrates all programming configs programming/00-loader.org
General Common tools (completion, version control, etc.) programming/01-general.org
Web HTML/CSS/JavaScript/TypeScript development programming/02-web.org
Python Python development environment programming/03-python.org
Ruby Ruby and Rails development programming/04-ruby.org

3. Supported Languages & Frameworks

3.1. Web Development

  • HTML5, CSS3, SCSS, SASS, Less
  • JavaScript (ES6+), JSX, React
  • TypeScript, TSX
  • Vue, Angular, Svelte
  • Template engines (Nunjucks, Handlebars, Mustache)

3.2. Programming Languages

  • Python (with venv, black, isort, pytest)
  • Ruby (with Rails, RSpec, RuboCop)
  • Clojure (CIDER, REPL)
  • SQL (ejc-sql integration)

3.3. Development Tools

  • LSP (Language Server Protocol)
  • Tree-sitter (fast parsing)
  • Completion (Corfu)
  • Version control (Magit)
  • Syntax checking (Flymake)
  • Code formatting (various formatters)

4. Configuration Modules

4.1. Loader Configuration

4.2. Programming Configuration Loader

This file serves as the main entry point for all programming-related configurations. It loads language-specific modules and platform-specific tools.

4.2.1. Purpose

The loader (prog-conf.el) is responsible for:

  • Loading general programming settings (prog-general-conf)
  • Loading language-specific configurations (web, Python, Ruby, Lua)
  • Conditionally loading platform-specific tools
  • Managing Clojure, SQL, and tree-sitter integrations

4.2.2. Architecture

The programming configuration is modular:

prog-conf.el (this file)
├── prog-general-conf.el   → General programming settings
├── prog-web-conf.el       → Web development (HTML/CSS/JS/TS)
├── prog-python-conf.el    → Python development
├── prog-ruby-conf.el      → Ruby development
└── prog-lua-conf.el       → Lua development

4.2.3. Loader Implementation

;;; prog-conf.el --- Programming configuration loader -*- lexical-binding: t; -*-
;;; Commentary:
;;; Main programming configuration file that loads language-specific modules
;;; and platform-specific programming tools.
;;; Code:

;; Load general programming configuration
(require 'prog-general-conf)

;; Load language-specific configurations
(require 'prog-web-conf)
(require 'prog-python-conf)
(require 'prog-ruby-conf)
(require 'prog-lua-conf)

;; Platform-specific and additional tools
(unless (eq system-type 'windows-nt)
  ;; Clojure development (non-Windows only)
  ;; TODO: finish https://clojure-doc.org/articles/tutorials/introduction/
  (use-package cider
      :custom
      (nrepl-use-ssh-fallback-for-remote-hosts t))

  ;; SQL database interaction
  (use-package ejc-sql
      ;; Github: https://github.com/kostafey/ejc-sql?tab=readme-ov-file#mariadbconnection
      ;; M-x ejc-connect
      ;; M-x ejc-quit-connection -> it refreshes the connection
      :config
      (load (expand-file-name ".local/ejc-config.el" user-emacs-directory) 'noerror 'nomessage))

  ;; Tree-sitter support
  (use-package tree-sitter
      :config
      (global-tree-sitter-mode 1))
  (use-package tree-sitter-langs)
  (use-package treesit-auto
      :config
      (global-treesit-auto-mode 1)))

(provide 'prog-conf)
;;; prog-conf.el ends here

4.2.4. Non-Windows Tools

Several tools are only loaded on non-Windows systems:

  • Clojure Development (CIDER)
    • Interactive Clojure development environment
    • REPL integration
    • SSH support for remote development
  • SQL Database Interaction (ejc-sql)
    • Connect to databases directly from Emacs
    • Execute SQL queries
    • Load local configuration from ~/.emacs.d/.local/ejc-config.el
  • Tree-sitter
    • Fast, incremental parsing library
    • Better syntax highlighting
    • More accurate code navigation
    • Language grammar support via tree-sitter-langs
    • Automatic mode activation via treesit-auto

4.3. General Programming Tools

4.4. General Programming Configuration

Common programming tools and utilities that apply across all languages.

4.4.1. Overview

This module provides:

  • Compilation: Enhanced compilation mode with ANSI colors
  • Version Control: Magit and merge conflict resolution
  • Project Management: Built-in project.el with custom markers and Magit integration
  • Documentation: Eldoc for inline documentation
  • Code Quality: Flymake for syntax checking
  • Editing Utilities: Electric pairs, parenthesis matching
  • Multi-Cursor Editing: Multiple cursors and intelligent region expansion
  • Diff & Merge: Advanced diff viewing and merge conflict resolution
  • Code Snippets: Template expansion with YASnippet
  • LSP: Language Server Protocol support with Eglot
  • REST Client: HTTP API testing from Emacs

4.4.2. What's Included

Category Tools
Build Tools compile, makefile-mode
Version Control magit, smerge-mode
Project Management project.el with custom markers
Documentation eldoc, eldoc-box
Syntax Checking flymake
Editing electric-pair, paren
Multi-Cursor Editing multiple-cursors, expand-region
Diff & Merge diff-mode, ediff
Code Snippets yasnippet
LSP eglot
REST Client restclient
File Formats conf-mode, yaml-mode, powershell, sql-indent

4.4.3. Compilation and Build Tools

Enhanced compilation mode with ANSI color support for better readability of build output. Includes support for Makefiles and configuration files.

Features:

  • ANSI color filtering in compilation buffers
  • Auto-scroll compilation output
  • Always kill existing compilation before starting new one
  • Tree-sitter support for Makefiles
;;; prog-general-conf.el --- General programming configuration -*- lexical-binding: t; -*-
;;; Commentary:
;;; General programming tools: compilation, version control,
;;; syntax checking, and common programming utilities.
;;; Code:

;; Compilation
(use-package compile
  :ensure nil
  :custom
  (compilation-always-kill t)
  (compilation-scroll-output t)
  (ansi-color-for-compilation-mode t)
  :config
  (add-hook 'compilation-filter-hook #'ansi-color-compilation-filter))

;; Makefile
(use-package makefile-mode
  :ensure nil
  :mode "\\Makefile.*\\'"
  :defer 't
  :config
  (add-to-list 'treesit-language-source-alist '(make "https://github.com/alemuller/tree-sitter-make")))

;; Configuration files
(use-package conf-mode
  :ensure nil
  :mode ("\\.env\\..*\\'" "\\.env\\'")
  :init
  (add-to-list 'auto-mode-alist '("\\.env\\'" . conf-mode)))

;; SQL
(use-package sql-indent
  :hook
  (sql-mode . sqlind-minor-mode))

(use-package powershell :defer t)
(use-package yaml-mode :defer t)

4.4.4. Version Control

Magit is the premier Git interface for Emacs, providing a comprehensive and intuitive way to work with Git repositories. Smerge-mode helps resolve merge conflicts directly in buffers.

Features:

  • Full-featured Git interface with C-x g
  • Same-window display (vim-fugitive style)
  • File-specific operations with C-c g
  • Refined diffs with word-level changes
  • Auto-save repository buffers without prompts
  • Smerge for interactive merge conflict resolution
  • Navigate conflicts with C-c ^ n/p
  • Keep upper/lower changes with C-c ^ u/l
;; Version Control
(use-package magit
  :ensure t
  :bind (("C-x g" . magit-status)
         ("C-x C-g" . magit-status)
         ("C-c g" . magit-file-dispatch)) ;; Great for acting on the current file
  :custom
  ;; 1. Window Management:
  ;; Open Magit in the same window (like vim-fugitive) rather than splitting.
  ;; This prevents Magit from messing up your window configuration.
  (magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1)

  ;; 2. Safety & Workflow:
  ;; Save all modified buffers in the repo without asking every time.
  (magit-save-repository-buffers 'dontask)
  ;; Don't restore the window config when quitting Magit (let me stay where I am).
  (magit-bury-buffer-function #'magit-mode-quit-window)

  ;; 3. Appearance in the Status Buffer:
  ;; Refine the hunk headers to look cleaner.
  (magit-diff-refine-hunk t)
  ;; Show fine-grained differences within lines (word-wise diffs).
  (magit-diff-refine-ignore-whitespace t)

  :config
  ;; Optional: Add a hook to maximize the window when entering Magit Status
  ;; (uncomment if you prefer full-screen git management)
  ;; (add-hook 'magit-status-mode-hook #'delete-other-windows)
  )

(use-package smerge-mode
  :ensure nil
  :bind (:map smerge-mode-map
              ("C-c ^ u" . smerge-keep-upper)
              ("C-c ^ l" . smerge-keep-lower)
              ("C-c ^ n" . smerge-next)
              ("C-c ^ p" . smerge-previous)))

4.4.5. Project Management

Built-in project management system for working with projects defined by version control roots (Git, SVN, etc.) or manually specified directories.

Features:

  • Automatic project detection via version control
  • Project-aware file finding and searching
  • Switch between projects easily
  • Compile/recompile within project context
  • Shell and eshell integration per project
  • Remember recent projects

Key bindings:

  • C-x p f : Find file in project
  • C-x p g : Grep in project (requires ripgrep or grep)
  • C-x p d : Open Dired at project root
  • C-x p s : Open shell at project root
  • C-x p e : Open eshell at project root
  • C-x p p : Switch to another project
  • C-x p k : Kill all project buffers
  • C-x p C : Recompile in project (custom binding)
  • C-x p b : Switch to project buffer
;; Project Management
(use-package project
  :ensure nil
  :custom
  ;; Where to store the list of known projects
  (project-list-file (expand-file-name "projects" user-emacs-directory))

  ;; Use ripgrep if available, otherwise fall back to grep
  (project-switch-commands
   '((project-find-file "Find file" ?f)
     (project-find-regexp "Find regexp" ?g)
     (project-find-dir "Find directory" ?d)
     (project-dired "Dired" ?D)
     (project-vc-dir "VC-Dir" ?v)
     (project-shell "Shell" ?s)
     (project-eshell "Eshell" ?e)
     (magit-project-status "Magit" ?m)
     (project-compile "Compile" ?c)
     (project-switch-to-buffer "Switch buffer" ?b)))

  ;; Remember recent files per project
  (project-remember-projects-under "~/Projects" t)

  :bind
  (("C-x p C" . project-recompile)
   ("C-x p m" . magit-project-status)
   :map project-prefix-map
   ("m" . magit-project-status))

  :config
  ;; Add additional project root markers
  (defun fu/project-try-local (dir)
    "Try to find a project root in DIR by looking for marker files."
    (let ((markers '(".project" ".projectile" "package.json" "Cargo.toml"
                     "setup.py" "requirements.txt" "pom.xml" "build.gradle")))
      (cl-some (lambda (marker)
                 (when-let* ((root (locate-dominating-file dir marker)))
                   (cons 'transient root)))
               markers)))

  ;; Register the custom project finder
  (add-hook 'project-find-functions #'fu/project-try-local)

  ;; Ignore certain directories when finding projects
  (add-to-list 'project-vc-ignores "node_modules")
  (add-to-list 'project-vc-ignores "target")
  (add-to-list 'project-vc-ignores "build")
  (add-to-list 'project-vc-ignores "dist")
  (add-to-list 'project-vc-ignores ".venv")
  (add-to-list 'project-vc-ignores "__pycache__"))

4.4.6. Documentation

Eldoc provides inline documentation in the echo area or popup boxes, showing function signatures, variable documentation, and more.

Features:

  • Global eldoc mode for all programming buffers
  • Popup documentation boxes with eldoc-box
  • Hover documentation with C-c h .
  • Multiline documentation support
  • Scrollable documentation windows
;; Documentation
(use-package eldoc
  :ensure nil
  :init
  (global-eldoc-mode))

;; (use-package eldoc-box
;;   :defer t
;;   :custom
;;   (eldoc-box-border-width 1)
;;   (eldoc-box-echo-area-use-multiline-p t)
;;   (eldoc-box-scrollbar-width 8)
;;   (eldoc-box-max-lines 10)
;;   (eldoc-box-use-echo-area nil)
;;   :bind
;;   ("C-c h ." . eldoc-box-help-at-point)
;;   :hook
;;   (eldoc-mode . eldoc-box-hover-mode))

4.4.7. Editing Utilities

Essential editing enhancements for programming including automatic pairing of brackets, parenthesis matching, and visual delimiter highlighting.

Features:

  • Electric pair mode: Auto-insert closing brackets, quotes, parentheses
  • Show matching parentheses with no delay
  • Mixed style: highlight expression when point is inside
  • Show context when matching paren is off-screen
;; Editing utilities
(use-package elec-pair
  :ensure nil
  :defer
  :hook
  (after-init-hook . electric-pair-mode))

(use-package paren
  :ensure nil
  :hook
  (after-init-hook . show-paren-mode)
  :custom
  (show-paren-delay 0)
  (show-paren-style 'mixed)
  (show-paren-context-when-offscreen t))

4.4.8. Syntax Checking

Flymake provides on-the-fly syntax checking with visual indicators in the margin and optional diagnostics at end of line.

Features:

  • Automatic syntax checking in programming modes
  • Margin indicators for errors/warnings/notes
  • Navigate errors with M-7 / M-8 or C-c ! n / C-c ! p
  • Toggle end-of-line diagnostics with C-c ! t
  • View all diagnostics with C-c ! l
;; Syntax checking
(use-package flymake
  :ensure nil
  :defer t
  :hook ((prog-mode . flymake-mode)
         (elisp-mode . flymake-mode))
  :bind (:map flymake-mode-map
              ("M-8" . flymake-goto-next-error)
              ("M-7" . flymake-goto-prev-error)
              ("C-c ! n" . flymake-goto-next-error)
              ("C-c ! p" . flymake-goto-prev-error)
              ("C-c ! l" . flymake-show-buffer-diagnostics)
              ("C-c ! t" . toggle-flymake-diagnostics-at-eol))
  :custom
  (flymake-show-diagnostics-at-end-of-line nil)
  (flymake-indicator-type 'margins)
  (flymake-margin-indicators-string
   `((error   "!" compilation-error)
       (warning "?" compilation-warning)
       (note    "i" compilation-info)))
  :config
  (defun toggle-flymake-diagnostics-at-eol ()
      "Toggle the display of Flymake diagnostics at the end of the line
and restart Flymake to apply the changes."
      (interactive)
      (setq flymake-show-diagnostics-at-end-of-line
          (not flymake-show-diagnostics-at-end-of-line))
      (flymake-mode -1)
      (flymake-mode 1)
      (message "Flymake diagnostics at end of line: %s"
             (if flymake-show-diagnostics-at-end-of-line
                 "Enabled" "Disabled"))))

4.4.9. Diff and Merge

Tools for viewing differences between files and resolving merge conflicts with enhanced syntax highlighting and navigation.

Features:

  • Read-only diff buffers by default (safer)
  • Auto-advance after applying hunks
  • Live updates of diffs
  • Syntax highlighting in diff hunks
  • Horizontal split for ediff
  • Plain window setup (no separate control frame)
;; Diff and merge
(use-package diff-mode
  :ensure nil
  :defer t
  :config
  (setq diff-default-read-only t)
  (setq diff-advance-after-apply-hunk t)
  (setq diff-update-on-the-fly t)
  (setq diff-font-lock-syntax 'hunk-also)
  (setq diff-font-lock-prettify nil))

(use-package ediff
  :ensure nil
  :commands (ediff-buffers ediff-files ediff-buffers3 ediff-files3)
  :init
  (setq ediff-split-window-function 'split-window-horizontally)
  (setq ediff-window-setup-function 'ediff-setup-windows-plain)
  :config
  (setq ediff-keep-variants nil)
  (setq ediff-make-buffers-readonly-at-startup nil)
  (setq ediff-merge-revisions-with-ancestor t)
  (setq ediff-show-clashes-only t))

4.4.10. Code Snippets

YASnippet provides a template system for Emacs, allowing you to type abbreviations and expand them into code templates.

Features:

  • Automatic activation in programming modes
  • Tab-stop navigation through template fields
  • Wide library of pre-built snippets for many languages
  • Easy to create custom snippets
;; Snippets
(use-package yasnippet
  :hook
  (prog-mode . yas-minor-mode))

4.4.11. LSP - Language Server Protocol

Eglot is a lightweight LSP client for Emacs providing IDE-like features including code completion, navigation, and refactoring.

Features:

  • Minimal configuration required
  • Auto-shutdown when buffers are killed
  • Code actions with C-c l a
  • Organize imports with C-c l o
  • Rename symbols with C-c l r
  • Format code with C-c l f
  • Performance optimized (disabled event logging)
;; LSP - Eglot
(use-package eglot
  :ensure nil
  :custom
  (eglot-autoshutdown t)
  (eglot-events-buffer-size 0)
  (eglot-events-buffer-config '(:size 0 :format full))
  (eglot-prefer-plaintext t)
  (jsonrpc-event-hook nil)
  (eglot-code-action-indications nil)
  (eglot-ignored-server-capabilities '(:workspaceFolders))
  :init
  (fset #'jsonrpc--log-event #'ignore)
  :bind (:map eglot-mode-map
               ("C-c l a" . eglot-code-actions)
               ("C-c l o" . eglot-code-actions-organize-imports)
               ("C-c l r" . eglot-rename)
               ("C-c l f" . eglot-format)))

4.4.12. Multiple Cursors and Region Expansion

Powerful editing features for working with multiple selections and intelligently expanding/contracting selections.

  • Multiple Cursors

    The multiple-cursors package allows you to edit multiple locations simultaneously, perfect for renaming variables or making repetitive edits.

    Key bindings:

    • C-> : Mark next like this (add cursor to next occurrence)
    • C-< : Mark previous like this (add cursor to previous occurrence)
    • C-c C-> : Mark all like this in buffer
    • C-S-<mouse-1> : Add cursor with mouse click
    • C-c m r : Mark all in region matching selection
    • C-c m l : Edit lines (add cursor to each line in region)
    ;; Multiple Cursors
    (use-package multiple-cursors
      :ensure t
      :bind (("C->" . mc/mark-next-like-this)
             ("C-<" . mc/mark-previous-like-this)
             ("C-c C->" . mc/mark-all-like-this)
             ("C-S-<mouse-1>" . mc/add-cursor-on-click)
             ("C-c m r" . mc/mark-all-in-region)
             ("C-c m l" . mc/edit-lines)))
    
  • Expand Region

    The expand-region package provides smart selection expansion based on semantic units (words, expressions, functions, etc.).

    Usage:

    • C-= : Expand region by semantic units
    • C-- : Contract region
    • Press repeatedly to expand/contract further

    Example: Place cursor in a word, press C-= once for word, again for quotes, again for expression, again for function, etc.

    ;; Expand Region
    (use-package expand-region
      :ensure t
      :bind (("C-=" . er/expand-region)
             ("C--" . er/contract-region)))
    

4.4.13. REST Client

HTTP REST client for testing APIs directly from Emacs buffers.

Features:

  • Write HTTP requests in a simple DSL
  • Execute requests and view responses inline
  • Support for variables and request chaining
  • Works with .rest files
;; REST client
(use-package restclient
  :mode ("\\.rest\\'" . restclient-mode)
  :commands (restclient-mode))

;; Claude Code integration
;; (use-package claude-code-emacs-client
;;   :ensure nil
;;   :load-path "~/Projects/claude-code-emacs-client"
;;   :custom
;;   (claude-code-default-permission-mode "acceptEdits")
;;   :bind
;;   (("C-c c n" . claude-code-new-session)
;;    ("C-c c r" . claude-code-resume-session)
;;    ("C-c c c" . claude-code-continue-session)
;;    ("C-c c m" . claude-code-send-message)
;;    ("C-c c s" . claude-code-send-region)))

4.5. End of File

(provide 'prog-general-conf)
;;; prog-general-conf.el ends here

4.6. Web Development

4.7. Web Development Configuration

Configuration for modern web development including HTML, CSS, JavaScript, TypeScript, and frameworks.

4.7.1. Overview

This module provides comprehensive support for:

  • HTML: Multiple modes (web-mode, mhtml-mode) with smart selection
  • CSS: Enhanced editing with completion and formatting
  • JavaScript: Modern JS/JSX with proper indentation and tooling
  • TypeScript: Full TypeScript and TSX support
  • Frameworks: React, Vue, Svelte, Angular
  • Tools: ESLint, Prettier, Language Server Protocol (LSP)
  • Build Tools: npm, yarn, webpack integration

4.7.2. Key Features

  • Smart Mode Selection

    Automatically chooses the best HTML mode based on file size:

    • Small files (< 5000 lines): web-mode for rich features
    • Large files (≥ 5000 lines): mhtml-mode for performance
  • Supported File Types
    • HTML, XHTML
    • JavaScript (.js, .jsx, .mjs)
    • TypeScript (.ts, .tsx)
    • Vue (.vue)
    • Templates (.njk, .hbs, .mustache)
    • CSS, SCSS, SASS, Less
    • JSON
  • Development Tools
    Tool Purpose
    web-mode Unified mode for HTML/CSS/JS
    emmet-mode HTML/CSS abbreviation expansion
    tagedit Paredit-like editing for HTML tags
    prettier-js Code formatting
    eslint JavaScript linting
    lsp-mode Language Server Protocol support
    typescript-mode TypeScript language support

4.7.3. Web

;;; prog-web-conf.el --- Web development configuration -*- lexical-binding: t; -*-
;;; Commentary:
;;; Configuration for web development including HTML, CSS, JavaScript,
;;; TypeScript, React/JSX, and related tools.
;;; Code:

;; HTML tag editing
(use-package tagedit
  :hook
  ((mhtml-mode
      sgml-mode
      nxml-mode)
   . (lambda ()
         (tagedit-add-paredit-like-keybindings)
         (tagedit-mode 1))))

(use-package emmet-mode
  :hook (mhtml-mode sgml-mode nxml-mode))

;; Smart HTML mode selection
(defun fu-html-mode-selector ()
  "Choose between web-mode and mhtml-mode based on file size.
Use mhtml-mode for files larger than 5000 lines to prevent hanging."
  (let* ((file (buffer-file-name))
         (size-threshold 5000))
    (when (and file (file-exists-p file))
      (let ((line-count (count-lines (point-min) (point-max))))
        (if (> line-count size-threshold)
            (progn
              (message "Large HTML file (%d lines) detected, using mhtml-mode for performance" line-count)
              (mhtml-mode))
          (web-mode))))))

;; Register HTML files to use smart mode selector
(add-to-list 'auto-mode-alist '("\\.html?\\'" . fu-html-mode-selector))

;; web-mode: powerful mode for HTML, JSX, TSX, Vue, etc.
(use-package web-mode
  :mode (("\\.jsx?\\'" . web-mode)
         ("\\.tsx?\\'" . web-mode)
         ("\\.vue\\'" . web-mode)
         ("\\.njk\\'" . web-mode)
         ("\\.hbs\\'" . web-mode)
         ("\\.mustache\\'" . web-mode))
  :custom
  ;; Indentation
  (web-mode-markup-indent-offset 2)
  (web-mode-css-indent-offset 2)
  (web-mode-code-indent-offset 2)
  (web-mode-style-padding 2)
  (web-mode-script-padding 2)
  (web-mode-block-padding 0)

  ;; Syntax highlighting
  (web-mode-enable-css-colorization t)
  (web-mode-enable-auto-pairing t)
  (web-mode-enable-comment-keywords t)
  (web-mode-enable-current-element-highlight t)
  (web-mode-enable-current-column-highlight nil)

  ;; Content types
  (web-mode-content-types-alist
   '(("jsx" . "\\.js[x]?\\'")
     ("tsx" . "\\.ts[x]?\\'")))

  ;; Auto-close tags
  (web-mode-enable-auto-closing t)
  (web-mode-enable-auto-quoting t)

  ;; Comments
  (web-mode-comment-style 2)

  :config
  ;; Remove background colors from web-mode faces to use theme's default
  (set-face-attribute 'web-mode-block-face nil :background nil)
  (set-face-attribute 'web-mode-script-face nil :background nil)
  (set-face-attribute 'web-mode-style-face nil :background nil)
  (set-face-attribute 'web-mode-part-face nil :background nil)

  ;; Better JSX/TSX detection
  (defun fu-web-mode-hook ()
    "Hooks for Web mode."
    (setq web-mode-enable-part-face nil)
    (when (string-match-p "\\.[jt]sx?\\'" (buffer-file-name))
      (setq-local web-mode-content-type "jsx")
      (setq-local web-mode-enable-auto-indentation t)))

  (add-hook 'web-mode-hook #'fu-web-mode-hook))

;; Eglot configuration for web-mode
(with-eval-after-load 'eglot
  (defun fu-eglot-web-mode-server (&optional interactive)
    "Return appropriate language server for web-mode based on file extension.
INTERACTIVE is passed by Eglot but not used here."
    (let ((file (buffer-file-name)))
      (cond
       ((and file (string-match-p "\\.tsx?\\'" file))
        '("typescript-language-server" "--stdio"))
       ((and file (string-match-p "\\.jsx?\\'" file))
        '("typescript-language-server" "--stdio"))
       (t
        '("vscode-html-language-server" "--stdio")))))

  ;; Configure web-mode server selection with smart detection
  (add-to-list 'eglot-server-programs
               '(web-mode . fu-eglot-web-mode-server))

  ;; Add eglot hooks for web modes
  (add-hook 'mhtml-mode-hook #'eglot-ensure)
  (add-hook 'web-mode-hook #'eglot-ensure)
  (add-hook 'css-mode-hook #'eglot-ensure)
  (add-hook 'scss-mode-hook #'eglot-ensure)
  (add-hook 'js-mode-hook #'eglot-ensure)
  (add-hook 'js2-mode-hook #'eglot-ensure)
  (add-hook 'js-ts-mode-hook #'eglot-ensure)
  (add-hook 'rjsx-mode-hook #'eglot-ensure)
  (add-hook 'typescript-mode-hook #'eglot-ensure)
  (add-hook 'typescript-ts-mode-hook #'eglot-ensure)
  (add-hook 'tsx-ts-mode-hook #'eglot-ensure)
  (add-hook 'json-mode-hook #'eglot-ensure))

;; js2-mode: enhanced JavaScript editing
(use-package js2-mode
  :mode "\\.m?js\\'"
  :interpreter "node"
  :custom
  (js2-basic-offset 2)
  (js2-bounce-indent-p nil)
  (js2-strict-missing-semi-warning nil)
  (js2-missing-semi-one-line-override t)
  (js-indent-level 2)
  :config
  ;; Disable js2-mode's syntax checking in favor of Flymake/LSP
  (setq js2-mode-show-parse-errors nil)
  (setq js2-mode-show-strict-warnings nil))

;; rjsx-mode: React JSX support
(use-package rjsx-mode
  :mode ("components/.*\\.js\\'" "\\.jsx\\'")
  :custom
  (js2-basic-offset 2)
  (js2-strict-missing-semi-warning nil))

(use-package typescript-mode
  :mode "\\.ts\\'"
  :custom
  (typescript-indent-level 2))

;; TypeScript TSX mode
(use-package tsx-mode
  :ensure nil
  :mode "\\.tsx\\'"
  :config
  (add-to-list 'auto-mode-alist '("\\.tsx\\'" . tsx-ts-mode)))

;; JSON mode
(use-package json-mode
  :mode "\\.json\\'"
  :custom
  (json-reformat:indent-width 2)
  (js-indent-level 2))

;; CSS/SCSS modes
(use-package css-mode
  :ensure nil
  :mode "\\.css\\'"
  :custom
  (css-indent-offset 2))

(use-package scss-mode
  :mode "\\.scss\\'"
  :custom
  (css-indent-offset 2))

;; Add node_modules/.bin to exec-path
(use-package add-node-modules-path
  :hook ((web-mode . add-node-modules-path)
         (js-mode . add-node-modules-path)
         (js2-mode . add-node-modules-path)
         (rjsx-mode . add-node-modules-path)
         (typescript-mode . add-node-modules-path)
         (typescript-ts-mode . add-node-modules-path)
         (tsx-ts-mode . add-node-modules-path)))

;; npm/yarn integration
(use-package npm-mode
  :commands (npm-mode)
  :hook ((web-mode . npm-mode)
         (js-mode . npm-mode)
         (js2-mode . npm-mode)
         (typescript-mode . npm-mode))
  :init
  (defun fu-npm-mode-keybindings ()
    "Set up npm-mode keybindings."
    (when (boundp 'npm-mode-command-keymap)
      (define-key npm-mode-command-keymap "n" 'npm-mode-npm-run)
      (define-key npm-mode-command-keymap "i" 'npm-mode-npm-install)
      (define-key npm-mode-command-keymap "t" 'npm-mode-npm-test)
      (define-key npm-mode-command-keymap "b" 'npm-mode-npm-build)))
  (add-hook 'npm-mode-hook 'fu-npm-mode-keybindings))

;; Prettier formatting
(use-package prettier
  :commands (prettier-prettify)
  :custom
  (prettier-pre-warm 'full))

;; ESLint integration via Flymake
(use-package flymake-eslint
  :hook ((js-mode . flymake-eslint-enable)
         (js2-mode . flymake-eslint-enable)
         (rjsx-mode . flymake-eslint-enable)
         (typescript-mode . flymake-eslint-enable)
         (typescript-ts-mode . flymake-eslint-enable)
         (tsx-ts-mode . flymake-eslint-enable)
         (web-mode . flymake-eslint-enable)))

;; Import JavaScript modules
(use-package js-import
  :after js
  :config
  (define-key js-mode-map (kbd "C-c i i") 'js-import)
  (define-key js-mode-map (kbd "C-c i f") 'js-import-from-file))

;; Run Jest tests
(use-package jest
  :commands (jest jest-popup)
  :custom
  (jest-executable "npx jest")
  :config
  (with-eval-after-load 'js
    (define-key js-mode-map (kbd "C-c t j") 'jest)
    (define-key js-mode-map (kbd "C-c t p") 'jest-popup)))

;; Indium: JavaScript debugging via Chrome DevTools Protocol
(use-package indium
  :commands (indium-connect indium-launch indium-interaction-mode)
  :hook ((js-mode . indium-interaction-mode)
         (js2-mode . indium-interaction-mode)
         (rjsx-mode . indium-interaction-mode))
  :custom
  (indium-chrome-executable
   (cond
    ((eq system-type 'darwin)
     "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome")
    ((eq system-type 'windows-nt)
     "C:/Program Files/Google/Chrome/Application/chrome.exe")
    (t "google-chrome")))
  :config
  (with-eval-after-load 'indium-interaction
    (define-key indium-interaction-mode-map (kbd "C-c C-c") 'indium-eval-defun)
    (define-key indium-interaction-mode-map (kbd "C-c C-e") 'indium-eval-last-node)
    (define-key indium-interaction-mode-map (kbd "C-c C-r") 'indium-eval-region)
    (define-key indium-interaction-mode-map (kbd "C-c C-b") 'indium-toggle-breakpoint)
    (define-key indium-interaction-mode-map (kbd "C-c C-l") 'indium-list-breakpoints)))

;; Skewer: Live web development
(use-package skewer-mode
  :commands (skewer-mode run-skewer skewer-css-mode skewer-html-mode)
  :hook ((js-mode . skewer-mode)
         (css-mode . skewer-css-mode)
         (web-mode . skewer-html-mode))
  :config
  (with-eval-after-load 'skewer-mode
    (define-key skewer-mode-map (kbd "C-c C-k") 'skewer-load-buffer)
    (define-key skewer-mode-map (kbd "C-c C-e") 'skewer-eval-last-expression)
    (define-key skewer-mode-map (kbd "C-c C-r") 'skewer-eval-region)))

;; Simple-httpd: built-in web server
(use-package simple-httpd
  :commands (httpd-start httpd-stop)
  :custom
  (httpd-port 8000)
  (httpd-host "localhost"))

(provide 'prog-web-conf)
;;; prog-web-conf.el ends here
  • Required Local Dependencies
    • Language Servers for Eglot
      # TypeScript/JavaScript Language Server
      npm install -g typescript typescript-language-server
      
      # HTML/CSS/JSON Language Servers (vscode-langservers-extracted)
      npm install -g vscode-langservers-extracted
      
      # Tailwind CSS Language Server (optional, for Tailwind projects)
      npm install -g @tailwindcss/language-server
      
    • Code Formatting and Linting
      # Prettier (code formatter)
      npm install -g prettier
      
      # ESLint (JavaScript/TypeScript linter)
      npm install -g eslint
      
      # For project-specific configs, install locally:
      # npm install --save-dev prettier eslint eslint-config-prettier
      
    • Debugging Tools
      # Chrome/Chromium browser for Indium debugging
      # macOS: Install Google Chrome from https://www.google.com/chrome/
      # Windows: Install Google Chrome from https://www.google.com/chrome/
      # Linux: sudo apt install google-chrome-stable
      
  • Emacs Packages

    All packages are installed automatically via use-package. Key packages include:

    • web-mode: Universal mode for HTML, JSX, TSX, Vue
    • js2-mode: Enhanced JavaScript editing
    • rjsx-mode: React JSX support
    • typescript-mode: TypeScript support
    • prettier: Code formatting
    • flymake-eslint: ESLint integration
    • npm-mode: npm/yarn command integration
    • add-node-modules-path: Automatic project-local tool detection
    • indium: Chrome DevTools debugging (alternative to dap-mode)
    • skewer-mode: Live browser interaction
    • restclient: HTTP API testing
  • Key Bindings
    • Eglot (LSP) - Available in all web modes
      • C-c l a - Code actions
      • C-c l o - Organize imports
      • C-c l r - Rename symbol
      • C-c l f - Format buffer/region
    • npm-mode

      npm-mode provides a prefix keymap for npm commands. Use M-x npm-mode-npm-run or access via the command keymap:

      • n (in npm-mode-command-keymap) - Run npm script
      • i (in npm-mode-command-keymap) - npm install
      • t (in npm-mode-command-keymap) - npm test
      • b (in npm-mode-command-keymap) - npm build

      Or use M-x npm-mode-npm-run, M-x npm-mode-npm-install, etc. directly

    • Jest Testing
      • C-c t j - Run Jest tests
      • C-c t p - Jest test popup
    • JavaScript Import
      • C-c i i - Import module
      • C-c i f - Import from file
    • Indium Debugging
      • C-c C-b - Toggle breakpoint
      • C-c C-c - Evaluate function
      • C-c C-e - Evaluate expression
      • C-c C-r - Evaluate region
      • C-c C-l - List breakpoints
    • Skewer (Live Development)
      • C-c C-k - Load buffer to browser
      • C-c C-e - Evaluate expression in browser
      • C-c C-r - Evaluate region in browser
  • Workflow Examples
    • Starting a React/Next.js Project
      1. Open project in Emacs
      2. M-x npm-mode-npm-install (or C-c n i) to install dependencies
      3. M-x npm-mode-npm-run (or C-c n n) and select "dev" to start dev server
      4. Open .jsx or .tsx files - web-mode will activate automatically
      5. Eglot will connect to typescript-language-server automatically
      6. Prettier will format on save (if enabled in project)
    • Debugging with Indium
      1. Start your dev server (e.g., npm run dev)
      2. M-x indium-launch to start Chrome with debugging enabled
      3. Set breakpoints with C-c C-b
      4. Interact with page, debugger will pause at breakpoints
      5. Evaluate expressions in context with C-c C-e
    • Live Development with Skewer
      1. M-x run-skewer to start the server
      2. Open http://localhost:8080 in browser
      3. Edit HTML/CSS/JS files
      4. C-c C-k to instantly reload changes in browser
    • Testing APIs with restclient
      1. Create a .rest file
      2. Write HTTP requests:

        GET https://api.example.com/users
        Content-Type: application/json
        
        ###
        
        POST https://api.example.com/users
        Content-Type: application/json
        
        {
          "name": "John Doe",
          "email": "john@example.com"
        }
        
      3. C-c C-c on a request to execute it
  • Project Structure Recommendations

    For optimal Emacs integration, use these files in your projects:

    • .prettierrc.json
      {
        "semi": true,
        "trailingComma": "es5",
        "singleQuote": true,
        "printWidth": 80,
        "tabWidth": 2
      }
      
    • .eslintrc.json
      {
        "extends": [
          "eslint:recommended",
          "plugin:react/recommended",
          "plugin:@typescript-eslint/recommended",
          "prettier"
        ],
        "parser": "@typescript-eslint/parser",
        "plugins": ["react", "@typescript-eslint"],
        "rules": {
          "react/react-in-jsx-scope": "off"
        }
      }
      
    • tsconfig.json (for TypeScript projects)
      {
        "compilerOptions": {
          "target": "ES2020",
          "lib": ["ES2020", "DOM"],
          "jsx": "react-jsx",
          "module": "ESNext",
          "moduleResolution": "bundler",
          "strict": true,
          "esModuleInterop": true,
          "skipLibCheck": true
        }
      }
      

4.8. Python Development

4.9. Python Development Configuration

Configuration for Python development with modern tooling and best practices.

4.9.1. Overview

This module provides Python development support including:

  • Language Support: python-mode with tree-sitter
  • Virtual Environments: pyvenv for virtualenv management
  • Code Quality: Black formatting, isort import sorting
  • Testing: pytest integration
  • REPL: Enhanced Python REPL
  • LSP: Python Language Server support

4.9.2. Key Features

  • Python Mode & Tree-Sitter
    • Syntax highlighting (enhanced with tree-sitter)
    • Intelligent indentation
    • Code navigation
    • Integration with debugging tools
    • Automatic mode selection based on tree-sitter availability
  • Virtual Environment Management
    • Automatic venv detection
    • Easy switching between environments
    • Integration with project workflows
  • Code Formatting & Quality
    Tool Purpose
    black Opinionated code formatter
    isort Import statement organizer
    flake8 Style guide enforcement
    pylint Code analysis
  • Development Workflow
    1. Virtual environment activation
    2. LSP server connection
    3. Auto-formatting on save
    4. Test runner integration

4.9.3. Python

;;; prog-python-conf.el --- Python development configuration -*- lexical-binding: t; -*-
;;; Commentary:
;;; Configuration for Python development including LSP support, formatting,
;;; testing, virtual environments, and debugging.
;;; Code:

;; Python mode settings
;; (use-package python
;;   :ensure nil
;;   :mode ("\\.py\\'" . python-mode)
;;   :interpreter ("python" "python3")
;;   :custom
;;   (python-indent-offset 4)
;;   (python-indent-guess-indent-offset nil)
;;   (python-shell-interpreter "python3")
;;   (python-shell-completion-native-enable nil)  ; Avoid readline issues
;;   :config
;;   ;; Remove trailing whitespace on save
;;   (add-hook 'python-mode-hook
;;             (lambda ()
;;               (add-hook 'before-save-hook #'delete-trailing-whitespace nil t))))

;; Python Tree-Sitter mode configuration
;; Emacs 29+ includes built-in tree-sitter support
(use-package python
  :ensure nil
  :mode ("\\.py\\'" . python-ts-mode)
  :interpreter ("python" . python-ts-mode)
  :custom
  (python-indent-offset 4)
  (python-indent-guess-indent-offset nil)
  (python-shell-interpreter "python3")
  (python-shell-completion-native-enable nil)  ; Avoid readline issues
  :init
  ;; Remap python-mode to python-ts-mode when tree-sitter is available
  (when (and (fboundp 'treesit-available-p)
             (treesit-available-p))
    (add-to-list 'major-mode-remap-alist '(python-mode . python-ts-mode)))

  :config
  ;; Ensure tree-sitter grammar is installed
  (defun fu-python-ensure-treesit-grammar ()
    "Ensure Python tree-sitter grammar is installed."
    (when (and (fboundp 'treesit-available-p)
               (treesit-available-p)
               (not (treesit-language-available-p 'python)))
      (message "Python tree-sitter grammar not found. Install with M-x treesit-install-language-grammar RET python RET")))

  (add-hook 'after-init-hook #'fu-python-ensure-treesit-grammar)

  ;; Common settings for both python-mode and python-ts-mode
  (defun fu-python-common-setup ()
    "Common setup for Python modes."
    (setq-local tab-width 4)
    (setq-local indent-tabs-mode nil)
    ;; Remove trailing whitespace on save
    (add-hook 'before-save-hook #'delete-trailing-whitespace nil t))

  (add-hook 'python-mode-hook #'fu-python-common-setup)
  (add-hook 'python-ts-mode-hook #'fu-python-common-setup))

;; Virtual environment management
(use-package pyvenv
  :defer t
  :config
  (setq pyvenv-mode-line-indicator '(pyvenv-virtual-env-name ("[venv:" pyvenv-virtual-env-name "] ")))

  ;; Automatically activate virtual environment
  (defun fu-python-auto-activate-venv ()
    "Automatically activate Python virtual environment if found.
Looks for .venv, venv, env directories or .python-version file."
    (interactive)
    (let* ((project-root (or (locate-dominating-file default-directory ".venv")
                             (locate-dominating-file default-directory "venv")
                             (locate-dominating-file default-directory "env")
                             (locate-dominating-file default-directory ".python-version")))
           (venv-path (when project-root
                        (or (expand-file-name ".venv" project-root)
                            (expand-file-name "venv" project-root)
                            (expand-file-name "env" project-root)))))
      (when (and venv-path (file-directory-p venv-path))
        (pyvenv-activate venv-path)
        (message "Activated virtual environment: %s" venv-path))))

  (add-hook 'python-mode-hook #'fu-python-auto-activate-venv)
  (add-hook 'python-ts-mode-hook #'fu-python-auto-activate-venv))

;; Python formatting with Black
(use-package python-black
  :defer t
  :commands (python-black-buffer python-black-region python-black-on-save-mode)
  :hook ((python-mode python-ts-mode) . python-black-on-save-mode-enable-dwim)
  :config
  (defun python-black-on-save-mode-enable-dwim ()
    "Enable python-black-on-save-mode if black is available."
    (when (executable-find "black")
      (python-black-on-save-mode 1))))

;; Import sorting with isort
(use-package py-isort
  :defer t
  :commands (py-isort-buffer py-isort-region)
  :hook (before-save . fu-python-isort-before-save)
  :config
  (defun fu-python-isort-before-save ()
    "Run isort before saving if in python-mode or python-ts-mode and isort is available."
    (when (and (derived-mode-p 'python-mode 'python-ts-mode)
               (executable-find "isort"))
      (py-isort-buffer))))

;; Python REPL enhancements
(use-package python
  :ensure nil
  :config
  ;; Send region/buffer to Python REPL
  (defun fu-python-shell-send-region-or-buffer ()
    "Send the current region to Python REPL, or whole buffer if no region is active."
    (interactive)
    (if (use-region-p)
        (python-shell-send-region (region-beginning) (region-end))
      (python-shell-send-buffer)))

  (defun fu-python-shell-send-defun-and-step ()
    "Send the current function to Python REPL and move to next function."
    (interactive)
    (python-shell-send-defun nil)
    (python-nav-forward-defun))

  :bind ((:map python-mode-map
               ("C-c C-c" . fu-python-shell-send-region-or-buffer)
               ("C-c C-n" . fu-python-shell-send-defun-and-step)
               ("C-c C-r" . python-shell-send-region)
               ("C-c C-b" . python-shell-send-buffer)
               ("C-c C-z" . python-shell-switch-to-shell))
         (:map python-ts-mode-map
               ("C-c C-c" . fu-python-shell-send-region-or-buffer)
               ("C-c C-n" . fu-python-shell-send-defun-and-step)
               ("C-c C-r" . python-shell-send-region)
               ("C-c C-b" . python-shell-send-buffer)
               ("C-c C-z" . python-shell-switch-to-shell))))

;; Python debugger (pdb) integration
(use-package realgud
  :defer t
  :commands (realgud:pdb))

;; Optional: IPython integration
;; Uncomment if you prefer IPython over standard Python REPL
;; (use-package python
;;   :config
;;   (when (executable-find "ipython")
;;     (setq python-shell-interpreter "ipython"
;;           python-shell-interpreter-args "-i --simple-prompt --no-color-info"
;;           python-shell-prompt-regexp "In \\[[0-9]+\\]: "
;;           python-shell-prompt-output-regexp "Out\\[[0-9]+\\]: "
;;           python-shell-completion-setup-code
;;           "from IPython.core.completerlib import module_completion"
;;           python-shell-completion-module-string-code
;;           "';'.join(module_completion('''%s'''))\n"
;;           python-shell-completion-string-code
;;           "';'.join(get_ipython().Completer.all_completions('''%s'''))\n")))

;; LSP configuration with Eglot
(with-eval-after-load 'eglot
  ;; Prefer pyright over pylsp if available
  (when (executable-find "pyright-langserver")
    (add-to-list 'eglot-server-programs
                 '(python-mode . ("pyright-langserver" "--stdio"))))

  ;; Enable eglot for Python
  (add-hook 'python-mode-hook #'eglot-ensure)
  (add-hook 'python-ts-mode-hook #'eglot-ensure))

;; Flycheck for additional linting (optional, alongside Flymake)
;; (use-package flycheck
;;   :hook (python-mode . flycheck-mode)
;;   :config
;;   (setq flycheck-python-flake8-executable "python3"
;;         flycheck-python-pycompile-executable "python3"
;;         flycheck-python-pylint-executable "python3"))

;; Jupyter notebook support (optional)
;; (use-package ein
;;   :defer t
;;   :commands (ein:run ein:login))

(provide 'prog-python-conf)
;;; prog-python-conf.el ends here

4.10. Ruby Development

4.11. Ruby Development Configuration

Configuration for Ruby and Ruby on Rails development.

4.11.1. Overview

This module provides comprehensive Ruby development support:

  • Language Support: ruby-mode with enhanced features
  • Rails Integration: Full Ruby on Rails support
  • REPL: inf-ruby for interactive development
  • Testing: RSpec and minitest integration
  • Code Quality: RuboCop integration
  • Package Management: Bundler support

4.11.2. Key Features

  • Ruby Mode
    • Syntax highlighting
    • Intelligent indentation
    • Code navigation
    • Symbol navigation
  • Ruby on Rails
    • Rails-specific features
    • Generator integration
    • Migration helpers
    • View/controller switching
  • Development Tools
    Tool Purpose
    ruby-mode Core Ruby language support
    inf-ruby Interactive Ruby REPL
    rspec-mode RSpec test runner
    rubocop Ruby style checker
    bundler Gem dependency management
  • Workflow Integration
    1. Project detection (Rails vs. pure Ruby)
    2. Bundler gem management
    3. Test runner integration
    4. Code formatting and linting

4.11.3. Ruby

;;; prog-ruby-conf.el --- Ruby development configuration -*- lexical-binding: t; -*-
;;; Commentary:
;;; Configuration for Ruby and Ruby on Rails development including LSP support,
;;; testing, formatting, REPL, and Rails-specific tools.
;;; Code:

;; Ruby mode settings
(use-package ruby-mode
  :ensure nil
  :mode (("\\.rb\\'" . ruby-mode)
         ("Rakefile\\'" . ruby-mode)
         ("Gemfile\\'" . ruby-mode)
         ("Berksfile\\'" . ruby-mode)
         ("Vagrantfile\\'" . ruby-mode)
         ("\\.rake\\'" . ruby-mode)
         ("\\.gemspec\\'" . ruby-mode)
         ("\\.ru\\'" . ruby-mode)
         ("Guardfile\\'" . ruby-mode)
         ("Capfile\\'" . ruby-mode)
         ("\\.cap\\'" . ruby-mode)
         ("\\.thor\\'" . ruby-mode)
         ("\\.rabl\\'" . ruby-mode)
         ("Thorfile\\'" . ruby-mode)
         ("\\.jbuilder\\'" . ruby-mode)
         ("Podfile\\'" . ruby-mode)
         ("\\.podspec\\'" . ruby-mode)
         ("Puppetfile\\'" . ruby-mode)
         ("\\.arb\\'" . ruby-mode))
  :interpreter "ruby"
  :custom
  (ruby-indent-level 2)
  (ruby-indent-tabs-mode nil)
  (ruby-insert-encoding-magic-comment nil)  ; Don't auto-insert encoding comment
  (ruby-align-chained-calls nil)
  (ruby-deep-indent-paren nil)
  :config
  ;; Remove trailing whitespace on save
  (add-hook 'ruby-mode-hook
            (lambda ()
              (add-hook 'before-save-hook #'delete-trailing-whitespace nil t)))

  ;; Enhance Ruby mode with additional features
  (add-hook 'ruby-mode-hook
            (lambda ()
              (setq-local comment-auto-fill-only-comments t)
              (auto-fill-mode 1))))

;; inf-ruby: Ruby REPL integration
(use-package inf-ruby
  :after ruby-mode
  :hook ((ruby-mode . inf-ruby-minor-mode)
         (compilation-filter . inf-ruby-auto-enter))
  :bind (:map ruby-mode-map
              ("C-c C-s" . inf-ruby)
              ("C-c C-z" . inf-ruby-switch-from-compilation)
              ("C-c C-c" . fu-ruby-send-region-or-buffer)
              ("C-c C-d" . fu-ruby-send-definition)
              ("C-c C-l" . ruby-load-file)
              ("C-c C-r" . ruby-send-region))
  :config
  ;; Automatically start REPL when evaluating code
  (defun fu-ruby-send-region-or-buffer ()
    "Send the current region to Ruby REPL, or whole buffer if no region is active."
    (interactive)
    (unless (get-buffer-process inf-ruby-buffer)
      (inf-ruby))
    (if (use-region-p)
        (ruby-send-region (region-beginning) (region-end))
      (ruby-send-buffer)))

  (defun fu-ruby-send-definition ()
    "Send the current definition to Ruby REPL."
    (interactive)
    (unless (get-buffer-process inf-ruby-buffer)
      (inf-ruby))
    (save-excursion
      (ruby-end-of-defun)
      (let ((end (point)))
        (ruby-beginning-of-defun)
        (ruby-send-region (point) end)))))

;; RuboCop: Linting and formatting
(use-package rubocop
  :defer t
  :hook (ruby-mode . rubocop-mode)
  :bind (:map rubocop-mode-map
              ("C-c r a" . rubocop-autocorrect-current-file)
              ("C-c r A" . rubocop-autocorrect-project)
              ("C-c r f" . rubocop-check-current-file)
              ("C-c r p" . rubocop-check-project))
  :custom
  (rubocop-autocorrect-on-save nil)  ; Don't auto-correct on save by default
  (rubocop-prefer-system-executable t)
  :config
  ;; Auto-format with RuboCop if available
  (defun fu-ruby-rubocop-autocorrect-on-save ()
    "Auto-correct current file with RuboCop on save if rubocop is available."
    (when (and (eq major-mode 'ruby-mode)
               (executable-find "rubocop"))
      (rubocop-autocorrect-current-file)))

  ;; Uncomment to enable auto-format on save
  ;; (add-hook 'after-save-hook #'fu-ruby-rubocop-autocorrect-on-save))
  )

;; RSpec: Testing framework
(use-package rspec-mode
  :after ruby-mode
  :hook (ruby-mode . rspec-mode)
  :bind (:map ruby-mode-map
              ("C-c t t" . rspec-verify)
              ("C-c t f" . rspec-verify-single)
              ("C-c t m" . rspec-verify-matching)
              ("C-c t a" . rspec-verify-all)
              ("C-c t r" . rspec-rerun)
              ("C-c t c" . rspec-verify-continue)
              ("C-c t s" . rspec-toggle-spec-and-target))
  :custom
  (rspec-use-rake-when-possible nil)
  (rspec-spec-command "rspec")
  (rspec-use-spring-when-possible t)
  :config
  ;; Use bundle exec if Gemfile exists
  (defun fu-ruby-use-bundler-for-rspec ()
    "Use bundle exec for RSpec if Gemfile exists."
    (when (locate-dominating-file default-directory "Gemfile")
      (setq rspec-spec-command "bundle exec rspec")))

  (add-hook 'ruby-mode-hook #'fu-ruby-use-bundler-for-rspec))

;; Bundler: Dependency management
;; Note: bundler package is no longer available on MELPA
;; Using custom functions with compilation-mode instead
(defun fu-bundle-install ()
  "Run bundle install in the project root."
  (interactive)
  (let ((default-directory (or (locate-dominating-file default-directory "Gemfile")
                               default-directory)))
    (compile "bundle install")))

(defun fu-bundle-update ()
  "Run bundle update in the project root."
  (interactive)
  (let ((default-directory (or (locate-dominating-file default-directory "Gemfile")
                               default-directory)))
    (compile "bundle update")))

(defun fu-bundle-exec (command)
  "Run COMMAND via bundle exec in the project root."
  (interactive "sBundle exec: ")
  (let ((default-directory (or (locate-dominating-file default-directory "Gemfile")
                               default-directory)))
    (compile (concat "bundle exec " command))))

;; Rake: Task runner
(use-package rake
  :after ruby-mode
  :commands (rake rake-find-task rake-rerun)
  :bind (:map ruby-mode-map
              ("C-c k r" . rake)
              ("C-c k f" . rake-find-task)
              ("C-c k R" . rake-rerun)))

;; YAML mode for Rails config files
(use-package yaml-mode
  :mode (("\\.yml\\'" . yaml-mode)
         ("\\.yaml\\'" . yaml-mode)
         ("config/database.yml" . yaml-mode)
         ("config/routes.rb" . ruby-mode)))

;; Enhanced Ruby refactoring
(use-package ruby-refactor
  :defer t
  :hook (ruby-mode . ruby-refactor-mode-launch)
  :bind (:map ruby-refactor-mode-map
              ("C-c C-e m" . ruby-refactor-extract-to-method)
              ("C-c C-e v" . ruby-refactor-extract-local-variable)
              ("C-c C-e c" . ruby-refactor-extract-constant)
              ("C-c C-e l" . ruby-refactor-extract-to-let)))

;; Ruby block manipulation
;; Note: ruby-block package is no longer available on MELPA
;; show-paren-mode provides similar functionality for matching blocks

;; YARD documentation
(use-package yard-mode
  :after ruby-mode
  :hook (ruby-mode . yard-mode)
  :bind (:map ruby-mode-map
              ("C-c y" . yard-mode)))

;; Ruby debugging with realgud
(use-package realgud
  :defer t
  :commands (realgud:byebug realgud:pry))

;; Chruby for Ruby version management (if you use chruby)
(use-package chruby
  :defer t
  :commands (chruby-use chruby-use-corresponding)
  :config
  ;; Auto-detect Ruby version from .ruby-version
  (defun fu-ruby-chruby-auto ()
    "Auto-select Ruby version based on .ruby-version file."
    (when (and (executable-find "chruby-exec")
               (locate-dominating-file default-directory ".ruby-version"))
      (chruby-use-corresponding)))

  (add-hook 'ruby-mode-hook #'fu-ruby-chruby-auto))

;; RVM support (alternative to chruby)
;; (use-package rvm
;;   :defer t
;;   :config
;;   (rvm-use-default))

;; rbenv support (alternative to chruby/rvm)
;; (use-package rbenv
;;   :defer t
;;   :config
;;   (global-rbenv-mode))

;; ERB (Embedded Ruby) mode
(use-package web-mode
  :mode (("\\.erb\\'" . web-mode)
         ("\\.html\\.erb\\'" . web-mode))
  :config
  (setq web-mode-engines-alist
        '(("erb" . "\\.html\\.erb\\'"))))

;; Haml support
(use-package haml-mode
  :mode "\\.haml\\'")

;; Slim template support
(use-package slim-mode
  :mode "\\.slim\\'")

;; Rails console in Emacs
;; (defun fu-rails-console ()
;;   "Start Rails console in inf-ruby."
;;   (interactive)
;;   (let ((default-directory (or (projectile-rails-root)
;;                                default-directory)))
;;     (if (file-exists-p "bin/rails")
;;         (inf-ruby-console-run "bin/rails console" "rails")
;;       (inf-ruby-console-run "bundle exec rails console" "rails"))))

;; (defun fu-rails-server ()
;;   "Start Rails server in async shell."
;;   (interactive)
;;   (let ((default-directory (or (projectile-rails-root)
;;                                default-directory)))
;;     (async-shell-command
;;      (if (file-exists-p "bin/rails")
;;          "bin/rails server"
;;        "bundle exec rails server")
;;      "*rails-server*")))

;; Keybindings for Rails
;; (with-eval-after-load 'ruby-mode
;;   (define-key ruby-mode-map (kbd "C-c R r") 'fu-rails-console)
;;   (define-key ruby-mode-map (kbd "C-c R s") 'fu-rails-server))

;; Ruby hash syntax conversion helpers
(defun fu-ruby-toggle-hash-syntax ()
  "Toggle between old and new Ruby hash syntax."
  (interactive)
  (save-excursion
    (let ((bounds (bounds-of-thing-at-point 'symbol)))
      (when bounds
        (let* ((start (car bounds))
               (end (cdr bounds))
               (symbol (buffer-substring-no-properties start end)))
          (cond
           ;; Convert :symbol => to symbol:
           ((looking-back ":\\([a-z_][a-z0-9_]*\\) =>" (line-beginning-position))
            (let ((match-start (match-beginning 0)))
              (delete-region match-start (point))
              (insert (match-string 1) ":")))
           ;; Convert symbol: to :symbol =>
           ((looking-at "\\([a-z_][a-z0-9_]*\\):")
            (let ((symbol (match-string 1)))
              (delete-region (match-beginning 0) (match-end 0))
              (insert ":" symbol " =>")))))))))

(with-eval-after-load 'ruby-mode
  (define-key ruby-mode-map (kbd "C-c h") 'fu-ruby-toggle-hash-syntax))

;; LSP configuration with Eglot
(with-eval-after-load 'eglot
  ;; Configure ruby-lsp server (preferred) or solargraph
  (add-to-list 'eglot-server-programs '((ruby-mode ruby-ts-mode) "ruby-lsp"))

  ;; Alternative: Use solargraph instead
  ;; (add-to-list 'eglot-server-programs
  ;;              '((ruby-mode ruby-ts-mode) "solargraph" "stdio"))

  ;; Enable eglot for Ruby modes
  (add-hook 'ruby-mode-hook #'eglot-ensure)
  (add-hook 'ruby-ts-mode-hook #'eglot-ensure))

;; Ruby end-of-line comments alignment
(defun fu-ruby-align-end-comments ()
  "Align end-of-line comments in Ruby."
  (interactive)
  (align-regexp (region-beginning) (region-end) "\\(\\s-*\\)#" 1 1 t))

(provide 'prog-ruby-conf)
;;; prog-ruby-conf.el ends here