diff --git a/local-lisp/glsl-mode.el b/local-lisp/glsl-mode.el new file mode 100644 index 0000000..8068b75 --- /dev/null +++ b/local-lisp/glsl-mode.el @@ -0,0 +1,451 @@ +;;; glsl-mode.el --- major mode for Open GLSL shader files + +;; Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. +;; Copyright (C) 2011, 2014, 2019 Jim Hourihan +;; +;; Authors: Xavier.Decoret@imag.fr, +;; Jim Hourihan (updated for 4.6, etc) +;; Keywords: languages OpenGL GPU SPIR-V Vulkan +;; Version: 2.4 +;; X-URL: https://github.com/jimhourihan/glsl-mode +;; +;; Original X-URL http://artis.inrialpes.fr/~Xavier.Decoret/resources/glsl-mode/ + +;; This file is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3, or (at your option) +;; any later version. + +;; This file 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 General Public License for more details. + +;; For a full copy of the GNU General Public License +;; see . + +;;; Commentary: + +;; Major mode for editing OpenGLSL grammar files, usually files ending with +;; `.vert', `.frag', `.glsl', `.geom'. Is is based on c-mode plus some +;; features and pre-specified fontifications. +;; +;; Modifications from the 1.0 version of glsl-mode (jimhourihan): +;; * Removed original optimized regexps for font-lock-keywords and +;; replaced with keyword lists for easier maintenance +;; * Added customization group and faces +;; * Preprocessor faces +;; * Updated to GLSL 4.6 +;; * Separate deprecated symbols +;; * Made _ part of a word +;; * man page lookup at opengl.org + +;; This package provides the following features: +;; * Syntax coloring (via font-lock) for grammar symbols and +;; builtin functions and variables for up to GLSL version 4.6 +;; * Indentation for the current line (TAB) and selected region (C-M-\). +;; * Switching between file.vert and file.frag +;; with S-lefttab (via ff-find-other-file) +;; * interactive function glsl-find-man-page prompts for glsl built +;; in function, formats opengl.org url and passes to browse-url + +;;; Installation: + +;; This file requires Emacs-20.3 or higher and package cc-mode. + +;; If glsl-mode is not part of your distribution, put this file into your +;; load-path and the following into your ~/.emacs: +;; (autoload 'glsl-mode "glsl-mode" nil t) +;; (add-to-list 'auto-mode-alist '("\\.glsl\\'" . glsl-mode)) +;; (add-to-list 'auto-mode-alist '("\\.vert\\'" . glsl-mode)) +;; (add-to-list 'auto-mode-alist '("\\.frag\\'" . glsl-mode)) +;; (add-to-list 'auto-mode-alist '("\\.geom\\'" . glsl-mode)) + +;; Reference: +;; https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.4.60.pdf + +;;; Code: + +(provide 'glsl-mode) + +(eval-when-compile ; required and optional libraries + (require 'cc-mode) + (require 'find-file)) + +(require 'align) + +(defgroup glsl nil + "OpenGL Shading Language Major Mode" + :group 'languages) + +(defconst glsl-language-version "4.6" + "GLSL language version number.") + +(defconst gl-version "4.6" + "OpenGL major mode version number.") + +(defvar glsl-mode-menu nil "Menu for GLSL mode") + +(defvar glsl-mode-hook nil "GLSL mode hook") + +(defvar glsl-type-face 'glsl-type-face) +(defface glsl-type-face + '((t (:inherit font-lock-type-face))) "glsl: type face" + :group 'glsl) + +(defvar glsl-builtin-face 'glsl-builtin-face) +(defface glsl-builtin-face + '((t (:inherit font-lock-builtin-face))) "glsl: builtin face" + :group 'glsl) + +(defvar glsl-deprecated-builtin-face 'glsl-deprecated-builtin-face) +(defface glsl-deprecated-builtin-face + '((t (:inherit font-lock-warning-face))) "glsl: deprecated builtin face" + :group 'glsl) + +(defvar glsl-qualifier-face 'glsl-qualifier-face) +(defface glsl-qualifier-face + '((t (:inherit font-lock-keyword-face))) "glsl: qualifier face" + :group 'glsl) + +(defvar glsl-keyword-face 'glsl-keyword-face) +(defface glsl-keyword-face + '((t (:inherit font-lock-keyword-face))) "glsl: keyword face" + :group 'glsl) + +(defvar glsl-deprecated-keyword-face 'glsl-deprecated-keyword-face) +(defface glsl-deprecated-keyword-face + '((t (:inherit font-lock-warning-face))) "glsl: deprecated keyword face" + :group 'glsl) + +(defvar glsl-variable-name-face 'glsl-variable-name-face) +(defface glsl-variable-name-face + '((t (:inherit font-lock-variable-name-face))) "glsl: variable face" + :group 'glsl) + +(defvar glsl-deprecated-variable-name-face 'glsl-deprecated-variable-name-face) +(defface glsl-deprecated-variable-name-face + '((t (:inherit font-lock-warning-face))) "glsl: deprecated variable face" + :group 'glsl) + +(defvar glsl-reserved-keyword-face 'glsl-reserved-keyword-face) +(defface glsl-reserved-keyword-face + '((t (:inherit glsl-keyword-face))) "glsl: reserved keyword face" + :group 'glsl) + +(defvar glsl-preprocessor-face 'glsl-preprocessor-face) +(defface glsl-preprocessor-face + '((t (:inherit font-lock-preprocessor-face))) "glsl: preprocessor face" + :group 'glsl) + +(defcustom glsl-additional-types nil + "List of additional keywords to be considered types. These are +added to the `glsl-type-list' and are fontified using the +`glsl-type-face'. Examples of existing types include \"float\", \"vec4\", + and \"int\"." + :type '(repeat (string :tag "Type Name")) + :group 'glsl) + +(defcustom glsl-additional-qualifiers nil + "List of additional keywords to be considered qualifiers. These +are added to the `glsl-qualifier-list' and are fontified using +the `glsl-qualifier-face'. Examples of existing qualifiers +include \"const\", \"in\", and \"out\"." + :type '(repeat (string :tag "Qualifier Name")) + :group 'glsl) + +(defcustom glsl-additional-keywords nil + "List of additional GLSL keywords. These are added to the +`glsl-keyword-list' and are fontified using the +`glsl-keyword-face'. Example existing keywords include \"while\", +\"if\", and \"return\"." + :type '(repeat (string :tag "Keyword")) + :group 'glsl) + +(defcustom glsl-additional-built-ins nil + "List of additional functions to be considered built-in. These +are added to the `glsl-builtin-list' and are fontified using the +`glsl-builtin-face'." + :type '(repeat (string :tag "Keyword")) + :group 'glsl) + +(defvar glsl-mode-hook nil) + +(defvar glsl-mode-map + (let ((glsl-mode-map (make-sparse-keymap))) + (define-key glsl-mode-map [S-iso-lefttab] 'ff-find-other-file) + glsl-mode-map) + "Keymap for GLSL major mode.") + +(defcustom glsl-browse-url-function 'browse-url + "Function used to display GLSL man pages. E.g. browse-url, eww, w3m, etc" + :type 'function + :group 'glsl) + +(defcustom glsl-man-pages-base-url "http://www.opengl.org/sdk/docs/man/html/" + "Location of GL man pages." + :type 'string + :group 'glsl) + +;;;###autoload +(progn + (add-to-list 'auto-mode-alist '("\\.vert\\'" . glsl-mode)) + (add-to-list 'auto-mode-alist '("\\.frag\\'" . glsl-mode)) + (add-to-list 'auto-mode-alist '("\\.geom\\'" . glsl-mode)) + (add-to-list 'auto-mode-alist '("\\.glsl\\'" . glsl-mode))) + +(eval-and-compile + ;; These vars are useful for completion so keep them around after + ;; compile as well. The goal here is to have the byte compiled code + ;; have optimized regexps so its not done at eval time. + (defvar glsl-type-list + '("float" "double" "int" "void" "bool" "true" "false" "mat2" "mat3" + "mat4" "dmat2" "dmat3" "dmat4" "mat2x2" "mat2x3" "mat2x4" "dmat2x2" + "dmat2x3" "dmat2x4" "mat3x2" "mat3x3" "mat3x4" "dmat3x2" "dmat3x3" + "dmat3x4" "mat4x2" "mat4x3" "mat4x4" "dmat4x2" "dmat4x3" "dmat4x4" "vec2" + "vec3" "vec4" "ivec2" "ivec3" "ivec4" "bvec2" "bvec3" "bvec4" "dvec2" + "dvec3" "dvec4" "uint" "uvec2" "uvec3" "uvec4" "atomic_uint" + "sampler1D" "sampler2D" "sampler3D" "samplerCube" "sampler1DShadow" + "sampler2DShadow" "samplerCubeShadow" "sampler1DArray" "sampler2DArray" + "sampler1DArrayShadow" "sampler2DArrayShadow" "isampler1D" "isampler2D" + "isampler3D" "isamplerCube" "isampler1DArray" "isampler2DArray" + "usampler1D" "usampler2D" "usampler3D" "usamplerCube" "usampler1DArray" + "usampler2DArray" "sampler2DRect" "sampler2DRectShadow" "isampler2DRect" + "usampler2DRect" "samplerBuffer" "isamplerBuffer" "usamplerBuffer" + "sampler2DMS" "isampler2DMS" "usampler2DMS" "sampler2DMSArray" + "isampler2DMSArray" "usampler2DMSArray" "samplerCubeArray" + "samplerCubeArrayShadow" "isamplerCubeArray" "usamplerCubeArray" + "image1D" "iimage1D" "uimage1D" "image2D" "iimage2D" "uimage2D" "image3D" + "iimage3D" "uimage3D" "image2DRect" "iimage2DRect" "uimage2DRect" + "imageCube" "iimageCube" "uimageCube" "imageBuffer" "iimageBuffer" + "uimageBuffer" "image1DArray" "iimage1DArray" "uimage1DArray" + "image2DArray" "iimage2DArray" "uimage2DArray" "imageCubeArray" + "iimageCubeArray" "uimageCubeArray" "image2DMS" "iimage2DMS" "uimage2DMS" + "image2DMSArray" "iimage2DMSArray" "uimage2DMSArray")) + + (defvar glsl-qualifier-list + '("attribute" "const" "uniform" "varying" "buffer" "shared" "coherent" + "volatile" "restrict" "readonly" "writeonly" "layout" "centroid" "flat" + "smooth" "noperspective" "patch" "sample" "in" "out" "inout" + "invariant" "lowp" "mediump" "highp")) + + (defvar glsl-keyword-list + '("break" "continue" "do" "for" "while" "if" "else" "subroutine" + "discard" "return" "precision" "struct" "switch" "default" "case")) + + (defvar glsl-reserved-list + '("input" "output" "asm" "class" "union" "enum" "typedef" "template" "this" + "packed" "resource" "goto" "inline" "noinline" + "common" "partition" "active" "long" "short" "half" "fixed" "unsigned" "superp" + "public" "static" "extern" "external" "interface" + "hvec2" "hvec3" "hvec4" "fvec2" "fvec3" "fvec4" + "filter" "sizeof" "cast" "namespace" "using" + "sampler3DRect")) + + (defvar glsl-deprecated-qualifier-list + '("varying" "attribute")) ; centroid is deprecated when used with varying + + (defvar glsl-builtin-list + '("abs" "acos" "acosh" "all" "any" "anyInvocation" "allInvocations" + "allInvocationsEqual" "asin" "asinh" "atan" "atanh" + "atomicAdd" "atomicMin" "atomicMax" "atomicAnd" "atomicOr" + "atomicXor" "atomicExchange" "atomicCompSwap" + "atomicCounter" "atomicCounterDecrement" "atomicCounterIncrement" + "atomicCounterAdd" "atomicCounterSubtract" "atomicCounterMin" + "atomicCounterMax" "atomicCounterAnd" "atomicCounterOr" + "atomicCounterXor" "atomicCounterExchange" "atomicCounterCompSwap" + "barrier" "bitCount" "bitfieldExtract" "bitfieldInsert" "bitfieldReverse" + "ceil" "clamp" "cos" "cosh" "cross" "degrees" "determinant" "dFdx" "dFdy" + "dFdyFine" "dFdxFine" "dFdyCoarse" "dFdxCoarse" "distance" "dot" + "fwidthFine" "fwidthCoarse" + "EmitStreamVertex" "EmitStreamPrimitive" "EmitVertex" "EndPrimitive" + "EndStreamPrimitive" "equal" "exp" "exp2" "faceforward" "findLSB" + "findMSB" "floatBitsToInt" "floatBitsToUint" "floor" "fma" "fract" + "frexp" "fwidth" "greaterThan" "greaterThanEqual" "groupMemoryBarrier" + "imageAtomicAdd" "imageAtomicAnd" "imageAtomicCompSwap" "imageAtomicExchange" + "imageAtomicMax" "imageAtomicMin" "imageAtomicOr" "imageAtomicXor" + "imageLoad" "imageSize" "imageStore" "imulExtended" "intBitsToFloat" + "imageSamples" "interpolateAtCentroid" "interpolateAtOffset" "interpolateAtSample" + "inverse" "inversesqrt" "isinf" "isnan" "ldexp" "length" "lessThan" + "lessThanEqual" "log" "log2" "matrixCompMult" "max" "memoryBarrier" + "memoryBarrierAtomicCounter" "memoryBarrierBuffer" + "memoryBarrierShared" "memoryBarrierImage" "memoryBarrier" + "min" "mix" "mod" "modf" "normalize" "not" "notEqual" "outerProduct" + "packDouble2x32" "packHalf2x16" "packSnorm2x16" "packSnorm4x8" + "packUnorm2x16" "packUnorm4x8" "pow" "radians" "reflect" "refract" + "round" "roundEven" "sign" "sin" "sinh" "smoothstep" "sqrt" "step" "tan" + "tanh" "texelFetch" "texelFetchOffset" "texture" "textureGather" + "textureGatherOffset" "textureGatherOffsets" "textureGrad" "textureSamples" + "textureGradOffset" "textureLod" "textureLodOffset" "textureOffset" + "textureProj" "textureProjGrad" "textureProjGradOffset" "textureProjLod" + "textureProjLodOffset" "textureProjOffset" "textureQueryLevels" "textureQueryLod" + "textureSize" "transpose" "trunc" "uaddCarry" "uintBitsToFloat" + "umulExtended" "unpackDouble2x32" "unpackHalf2x16" "unpackSnorm2x16" + "unpackSnorm4x8" "unpackUnorm2x16" "unpackUnorm4x8" "usubBorrow")) + + (defvar glsl-deprecated-builtin-list + '("noise1" "noise2" "noise3" "noise4" + "texture1D" "texture1DProj" "texture1DLod" "texture1DProjLod" + "texture2D" "texture2DProj" "texture2DLod" "texture2DProjLod" + "texture2DRect" "texture2DRectProj" + "texture3D" "texture3DProj" "texture3DLod" "texture3DProjLod" + "shadow1D" "shadow1DProj" "shadow1DLod" "shadow1DProjLod" + "shadow2D" "shadow2DProj" "shadow2DLod" "shadow2DProjLod" + "textureCube" "textureCubeLod")) + + (defvar glsl-deprecated-variables-list + '("gl_FragColor" "gl_FragData" "gl_MaxVarying" "gl_MaxVaryingFloats" + "gl_MaxVaryingComponents")) + + (defvar glsl-preprocessor-directive-list + '("define" "undef" "if" "ifdef" "ifndef" "else" "elif" "endif" + "error" "pragma" "extension" "version" "line")) + + (defvar glsl-preprocessor-expr-list + '("defined" "##")) + + (defvar glsl-preprocessor-builtin-list + '("__LINE__" "__FILE__" "__VERSION__")) + + ) ; eval-and-compile + +(eval-and-compile + (defun glsl-ppre (re) + (format "\\<\\(%s\\)\\>" (regexp-opt re)))) + +(defvar glsl-font-lock-keywords-1 + (append + (list + (cons (eval-when-compile + (format "^[ \t]*#[ \t]*\\<\\(%s\\)\\>" + (regexp-opt glsl-preprocessor-directive-list))) + glsl-preprocessor-face) + (cons (eval-when-compile + (glsl-ppre glsl-type-list)) + glsl-type-face) + (cons (eval-when-compile + (glsl-ppre glsl-deprecated-qualifier-list)) + glsl-deprecated-keyword-face) + (cons (eval-when-compile + (glsl-ppre glsl-reserved-list)) + glsl-reserved-keyword-face) + (cons (eval-when-compile + (glsl-ppre glsl-qualifier-list)) + glsl-qualifier-face) + (cons (eval-when-compile + (glsl-ppre glsl-keyword-list)) + glsl-keyword-face) + (cons (eval-when-compile + (glsl-ppre glsl-preprocessor-builtin-list)) + glsl-keyword-face) + (cons (eval-when-compile + (glsl-ppre glsl-deprecated-builtin-list)) + glsl-deprecated-builtin-face) + (cons (eval-when-compile + (glsl-ppre glsl-builtin-list)) + glsl-builtin-face) + (cons (eval-when-compile + (glsl-ppre glsl-deprecated-variables-list)) + glsl-deprecated-variable-name-face) + (cons "gl_[A-Z][A-Za-z_]+" glsl-variable-name-face) + ) + + (when glsl-additional-types + (list + (cons (glsl-ppre glsl-additional-types) glsl-type-face))) + (when glsl-additional-keywords + (list + (cons (glsl-ppre glsl-additional-keywords) glsl-keyword-face))) + (when glsl-additional-qualifiers + (list + (cons (glsl-ppre glsl-additional-qualifiers) glsl-qualifier-face))) + (when glsl-additional-built-ins + (list + (cons (glsl-ppre glsl-additional-built-ins) glsl-builtin-face))) + ) + "Highlighting expressions for GLSL mode.") + + +(defvar glsl-font-lock-keywords glsl-font-lock-keywords-1 + "Default highlighting expressions for GLSL mode.") + +(defvar glsl-mode-syntax-table + (let ((glsl-mode-syntax-table (make-syntax-table))) + (modify-syntax-entry ?/ ". 124b" glsl-mode-syntax-table) + (modify-syntax-entry ?* ". 23" glsl-mode-syntax-table) + (modify-syntax-entry ?\n "> b" glsl-mode-syntax-table) + (modify-syntax-entry ?_ "w" glsl-mode-syntax-table) + glsl-mode-syntax-table) + "Syntax table for glsl-mode.") + +(defvar glsl-other-file-alist + '(("\\.frag$" (".vert")) + ("\\.vert$" (".frag")) + ) + "Alist of extensions to find given the current file's extension.") + +(defun glsl-man-completion-list () + "Return list of all GLSL keywords." + (append glsl-builtin-list glsl-deprecated-builtin-list)) + +(defun glsl-find-man-page (thing) + "Collects and displays manual entry for GLSL built-in function THING." + (interactive + (let ((word (current-word nil t))) + (list + (completing-read + (concat "OpenGL.org GLSL man page: (" word "): ") + (glsl-man-completion-list) + nil nil nil nil word)))) + (save-excursion + (apply glsl-browse-url-function + (list (concat glsl-man-pages-base-url thing ".xhtml"))))) + +(easy-menu-define glsl-menu glsl-mode-map + "GLSL Menu" + `("GLSL" + ["Comment Out Region" comment-region + (c-fn-region-is-active-p)] + ["Uncomment Region" (comment-region (region-beginning) + (region-end) '(4)) + (c-fn-region-is-active-p)] + ["Indent Expression" c-indent-exp + (memq (char-after) '(?\( ?\[ ?\{))] + ["Indent Line or Region" c-indent-line-or-region t] + ["Fill Comment Paragraph" c-fill-paragraph t] + "----" + ["Backward Statement" c-beginning-of-statement t] + ["Forward Statement" c-end-of-statement t] + "----" + ["Up Conditional" c-up-conditional t] + ["Backward Conditional" c-backward-conditional t] + ["Forward Conditional" c-forward-conditional t] + "----" + ["Backslashify" c-backslash-region (c-fn-region-is-active-p)] + "----" + ["Find GLSL Man Page" glsl-find-man-page t] + )) + +;;;###autoload +(define-derived-mode glsl-mode prog-mode "GLSL" + "Major mode for editing GLSL shader files." + (c-initialize-cc-mode t) + (setq abbrev-mode t) + (c-init-language-vars-for 'c-mode) + (c-common-init 'c-mode) + (cc-imenu-init cc-imenu-c++-generic-expression) + (set (make-local-variable 'font-lock-defaults) '(glsl-font-lock-keywords)) + (set (make-local-variable 'ff-other-file-alist) 'glsl-other-file-alist) + (set (make-local-variable 'comment-start) "// ") + (set (make-local-variable 'comment-end) "") + (set (make-local-variable 'comment-padding) "") + (easy-menu-add glsl-menu) + (add-to-list 'align-c++-modes 'glsl-mode) + (c-run-mode-hooks 'c-mode-common-hook) + (run-mode-hooks 'glsl-mode-hook) + :after-hook (progn (c-make-noise-macro-regexps) + (c-make-macro-with-semi-re) + (c-update-modeline)) + ) + +;;; glsl-mode.el ends here diff --git a/local-lisp/hlsl-mode.el b/local-lisp/hlsl-mode.el new file mode 100644 index 0000000..22f108f --- /dev/null +++ b/local-lisp/hlsl-mode.el @@ -0,0 +1,581 @@ +;;; hlsl-mode.el --- Major mode for HLSL shader files -*- lexical-binding: t; -*- + +;; Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. +;; Copyright (C) 2011, 2014, 2019 Jim Hourihan +;; Copyright (C) 2022 Jcaw +;; +;; Authors: Xavier.Decoret@imag.fr, +;; Jim Hourihan +;; GitHub user "jcaw" +;; Keywords: languages HLSL GPU shaders +;; Version: 1.1.3 +;; URL: https://github.com/jcaw/hlsl-mode +;; Package-Requires: ((emacs "24.4")) +;; +;; Original URL http://artis.inrialpes.fr/~Xavier.Decoret/resources/glsl-mode/ + +;; This file is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3, or (at your option) +;; any later version. + +;; This file 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 General Public License for more details. + +;; For a full copy of the GNU General Public License +;; see . + +;;; Commentary: + +;; Major mode for editing OpenHLSL grammar files, usually files ending with +;; `.fx[hc]', `.hlsl', `.shader', `.compute'. It is based on c-mode plus some +;; features and pre-specified fontifications. +;; +;; It is modified from `glsl-mode', maintained at the time of writing by Jim +;; Hourihan: https://github.com/jimhourihan/glsl-mode + +;; This package provides the following features: +;; * Syntax coloring (via font-lock) for grammar symbols and +;; builtin functions and variables for HLSL (up to the version specified +;; by `hlsl-shader-model-version') +;; * Indentation for the current line (TAB) and selected region (C-M-\). + +;;; Code: + +(require 'cc-mode) + +(eval-when-compile ; required and optional libraries + (require 'find-file) + (require 'subr-x)) + +(require 'align) + +(defgroup hlsl nil + "Microsoft HLSL Major Mode." + :group 'languages) + +(defconst hlsl-shader-model-version "6.0" + "Shader model version number.") + +(defconst hlsl-direct3d-version "12" + "Direct3D language version number.") + +(defvar hlsl-type-face 'hlsl-type-face) +(defface hlsl-type-face + '((t (:inherit font-lock-type-face))) "HLSL: type face." + :group 'hlsl) + +(defvar hlsl-builtin-face 'hlsl-builtin-face) +(defface hlsl-builtin-face + '((t (:inherit font-lock-builtin-face))) "HLSL: builtin face." + :group 'hlsl) + +(defvar hlsl-deprecated-builtin-face 'hlsl-deprecated-builtin-face) +(defface hlsl-deprecated-builtin-face + '((t (:inherit font-lock-warning-face))) "HLSL: deprecated builtin face." + :group 'hlsl) + +(defvar hlsl-qualifier-face 'hlsl-qualifier-face) +(defface hlsl-qualifier-face + '((t (:inherit font-lock-keyword-face))) "HLSL: qualifier face." + :group 'hlsl) + +(defvar hlsl-keyword-face 'hlsl-keyword-face) +(defface hlsl-keyword-face + '((t (:inherit font-lock-keyword-face))) "HLSL: keyword face." + :group 'hlsl) + +(defvar hlsl-deprecated-keyword-face 'hlsl-deprecated-keyword-face) +(defface hlsl-deprecated-keyword-face + '((t (:inherit font-lock-warning-face))) "HLSL: deprecated keyword face." + :group 'hlsl) + +(defvar hlsl-variable-name-face 'hlsl-variable-name-face) +(defface hlsl-variable-name-face + '((t (:inherit font-lock-variable-name-face))) "HLSL: variable face." + :group 'hlsl) + +(defvar hlsl-deprecated-variable-name-face 'hlsl-deprecated-variable-name-face) +(defface hlsl-deprecated-variable-name-face + '((t (:inherit font-lock-warning-face))) "HLSL: deprecated variable face." + :group 'hlsl) + +(defvar hlsl-reserved-keyword-face 'hlsl-reserved-keyword-face) +(defface hlsl-reserved-keyword-face + '((t (:inherit hlsl-keyword-face))) "HLSL: reserved keyword face." + :group 'hlsl) + +(defvar hlsl-preprocessor-face 'hlsl-preprocessor-face) +(defface hlsl-preprocessor-face + '((t (:inherit font-lock-preprocessor-face))) "HLSL: preprocessor face." + :group 'hlsl) + +(defcustom hlsl-additional-types nil + "List of additional keywords to be considered types. + +These are added to the `hlsl-type-list' and are fontified using +the `hlsl-type-face'. Examples of existing types include +\"float\", \"float4\", and \"RWStructuredBuffer\"." + :type '(repeat (string :tag "Type Name")) + :group 'hlsl) + +(defcustom hlsl-additional-qualifiers nil + "List of additional keywords to be considered qualifiers. + +These are added to the `hlsl-qualifier-list' and are fontified +using the `hlsl-qualifier-face'. Examples of existing qualifiers +include \"const\", \"in\", and \"out\"." + :type '(repeat (string :tag "Qualifier Name")) + :group 'hlsl) + +(defcustom hlsl-additional-keywords nil + "List of additional HLSL keywords. + +These are added to the `hlsl-keyword-list' and are fontified +using the `hlsl-keyword-face'. Example existing keywords include +\"while\", \"if\", and \"return\"." + :type '(repeat (string :tag "Keyword")) + :group 'hlsl) + +(defcustom hlsl-additional-built-ins nil + "List of additional functions to be considered built-in. + +These are added to the `hlsl-builtin-list' and are fontified +using the `hlsl-builtin-face'." + :type '(repeat (string :tag "Keyword")) + :group 'hlsl) + +(defvar hlsl-mode-map + (let ((hlsl-mode-map (make-sparse-keymap))) + (define-key hlsl-mode-map [S-iso-lefttab] 'ff-find-other-file) + hlsl-mode-map) + "Keymap for HLSL major mode.") + +;; (defcustom hlsl-browse-url-function 'browse-url +;; "Function used to display HLSL man pages. + +;; E.g. `browse-url', `eww', `w3m', etc." +;; :type 'function +;; :group 'hlsl) + +;; (defcustom hlsl-man-pages-base-url "http://www.opengl.org/sdk/docs/man/html/" +;; "Location of GL man pages." +;; :type 'string +;; :group 'hlsl) + +;;;###autoload +(progn + (add-to-list 'auto-mode-alist '("\\.fx\\'" . hlsl-mode)) + (add-to-list 'auto-mode-alist '("\\.fxc\\'" . hlsl-mode)) + (add-to-list 'auto-mode-alist '("\\.fxh\\'" . hlsl-mode)) + (add-to-list 'auto-mode-alist '("\\.hlsl\\'" . hlsl-mode)) + (add-to-list 'auto-mode-alist '("\\.cg\\'" . hlsl-mode)) + ;; Unity shader format + (add-to-list 'auto-mode-alist '("\\.shader\\'" . hlsl-mode)) + ;; Unity shader include suffix + (add-to-list 'auto-mode-alist '("\\.cginc\\'" . hlsl-mode)) + ;; Unity compute shaders are HLSL + (add-to-list 'auto-mode-alist '("\\.compute\\'" . hlsl-mode)) + ) + +(eval-and-compile + (defun hlsl--mixed-case (strings) + "Get original `strings', plus lowercase and uppercase versions. + +For example: + + (hlsl--mixed-case '(\"TexCoord\" \"POSITION\")) + -> '(\"TexCoord\" \"TEXCOORD\" \"Texcoord\" + \"POSITION\" \"Position\") + +Returns a flattened list of all the resultant strings (without +duplicates)." + (apply #'append (mapcar (lambda (s) + (if (= 0 (length s)) + s + (delete-dups + (list s (upcase s) + (concat (upcase (substring s 0 1)) + (downcase (substring s 1))))))) + strings))) + + ;; These vars are useful for completion so keep them around after compile as + ;; well. The goal here is to have the byte compiled code have optimized + ;; regexps so its not done at eval time. + (defvar hlsl-type-list + `( + ;; TODO: Maybe make `void' a keyword? + "matrix" "void" + + ;; Numeric scalars, plus all the vector and matrix expressions for each. E.g: + ;; float, float1, float1x2, uint2, bool3x4, etc. + ,@(mapcar (lambda (type) + (concat type "\\([1234]?\\|\\([1234]x[1234]\\)?\\)")) + '("bool" "int" "uint" "float" + "min16float" "min10float" "min16int" "min12int" "min16uint")) + ;; These numeric scalars don't expand to vectors/matrices + "half" "dword" "double" + + ;; Texture samplers + "sampler" "sampler1D" "sampler2D" "sampler3D" "samplerCUBE" "sampler_state" + "SamplerState" "SampleComparisonState" + + ;; Buffer-esque types + "AppendStructuredBuffer" "Buffer" "ByteAddressBuffer" "ConsumeStructuredBuffer" + "InputPatch" "OutputPatch" "RWBuffer" "RWByteAddressBuffer" "RWStructuredBuffer" + "RWTexture1D" "RWTexture1DArray" "RWTexture2D" "RWTexture2DArray" "RWTexture3D" + "StructuredBuffer" "Texture1D" "Texture1DArray" "Texture2D" "Texture2DArray" + "Texture2DMS" "Texture2DMSArray" "Texture3D" "TextureCube" "TextureCubeArray" + ;; Rasterizer Order Views + "RasterizerOrderedBuffer" "RasterizerOrderedByteAddressBuffer" "RasterizerOrderedStructuredBuffer" + "RasterizerOrderedTexture1D" "RasterizerOrderedTexture1DArray" "RasterizerOrderedTexture2D" + "RasterizerOrderedTexture2DArray" "RasterizerOrderedTexture3D" + + ;; Misc + ;; + ;; TODO: Double check Object2 usage + "Object2" + + ;; Geometry shader stream outputs + "PointStream" "LineStream" "TriangleStream")) + + (defvar hlsl-qualifier-list + '( + ;; Misc + "snorm" "unorm" "in" "inline" "inout" "precise" "extern" "nointerpolation" + "precise" "shared" "groupshared" "static" "uniform" "volatile" "const" + "row_major" "column_major" "export" "linear" "centroid" "noperspective" + "sample" "globallycoherent" "out" + + ;; Geom shader primitives + "point" "line" "triangle" "lineadj" "triangledj" + + "pixelfragment" "vertexfragment")) + + (defvar hlsl-keyword-list + `( + ;; Special types + "true" "false" "NULL" "null" + + ;; Misc + "compile_fragment" "class" "interface" "break" "continue" "do" "default" + "discard" "for" "while" "if" "else" "return" "struct" "switch" "case" + "register" "packoffset" "cbuffer" "tbuffer" "fxgroup" "pass" + "technique[1-9]?[1-9]?" + "SetVertexShader" "SetGeometryShader" "SetPixelSader" + + ;; Attributes + "maxvertexcount" "domain" "earlydepthstencil" "instance" "maxtessfactor" + "numthreads" "outputcontrolpoints" "outputtopology" "partitioning" + "patchconstantfunc" + ;; domain types + ;; TODO: move these into a dedicated domain matcher? + ;; TODO: `point' and `line' at least are duplicated here and in qualifiers + ;; TODO: Take another look at these, they should probably be handled differently + "tri" "quad" "isoline" "point" "line" "triangle_cw" "triangle_ccw" + "integer" "fractional_even" "fractional_odd" "pow2" + ;; Branching attributes + "unroll" "loop" "fastopt" "allow_uav_condition" "branch" "flatten" + "forcecase" "call" + + ;; Shader profiles + ;; + ;; Shader Model 1 + "vs_1_1" + ;; Shader Model 2 + "ps_2_0" "ps_2_x" "vs_2_0" "vs_2_x" "ps_4_0_level_9_0" "ps_4_0_level_9_1" + "ps_4_0_level_9_3" "vs_4_0_level_9_0" "vs_4_0_level_9_1" "vs_4_0_level_9_3" + "lib_4_0_level_9_1" "lib_4_0_level_9_" + ;; Shader Model 3 + "ps_3_0" "vs_3_0" + ;; Shader Model 4 + "cs_4_0" "gs_4_0" "ps_4_0" "vs_4_0" "cs_4_1" "gs_4_1" "ps_4_1" "vs_4_1" + "lib_4_0" "lib_4_" + ;; Shader Model 5 + "cs_5_0" "ds_5_0" "gs_5_0" "hs_5_0" "ps_5_0" "vs_5_0" "lib_5_" + ;; Shader Model 6 + "cs_6_0" "ds_6_0" "gs_6_0" "hs_6_0" "ps_6_0" "vs_6_0" "lib_6_" + ;; FX Profiles + "fx_1_0" "fx_2_0" "fx_4_0" "fx_4_1" "fx_5_0" + + ;; Semantics + ;; + ;; All semantics are case-insensitive, so allow uppercase, lowercase and + ;; capitalised versions. Other/weird mixtures of case will not be + ;; highlighted for now, even though the HLSL spec allows them. + ,@(hlsl--mixed-case + '("Binormal[0-9]?" "BlendIndices[0-9]?" "BlendWeight[0-9]?" "Color[0-9]?" + "Normal[0-9]?" "Position[0-9]?" "PositionT" "PSize[0-9]?" "Tangent[0-9]?" + "TexCoord[0-9]" "Fog" "TessFactor[0-9]?" "VFace" "VPos" "Depth[0-9]?" + "SV_ClipDistance[0-9]?" "SV_CullDistance[0-9]?" "SV_Coverage" "SV_Depth" + "SV_DepthGreaterEqual" "SV_DepthLessEqual" "SV_DispatchThreadID" + "SV_DomainLocation" "SV_GroupID" "SV_GroupIndex" "SV_GroupThreadID" + "SV_GSInstanceID" "SV_InnerCoverage" "SV_InsideTessFactor" "SV_InstanceID" + "SV_IsFrontFace" "SV_OutputControlPointID" "SV_Position" "SV_PrimitiveID" + "SV_RenderTargetArrayIndex" "SV_SampleIndex" "SV_StencilRef" "SV_Target[0-7]" + "SV_TessFactor" "SV_VertexID" "SV_ViewportArrayIndex" "SV_ShadingRate" + "SV_Target" + ;; Misc Semantics + "BezierPos" "WorldPos" "TanUCorner" "TanVCorner" "TanWeights" "Bitangent[0-9]?")) + + ;; Unity keywords + ;; + ;; TODO: Separate derived mode for Unity ".shader" files? They're not strictly HLSL. + "Shader" "Properties" "SubShader" "Tags" "Pass" "CGPROGRAM" "ENDCG")) + + (defvar hlsl-reserved-list + '( + ;; [2022/01/31] Just the reserved words found on: + ;; https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-appendix-reserved-words + "auto" "case" "catch" "char" "class" "const_cast" "default" "delete" "dynamic_cast" + "enum" "explicit" "friend" "goto" "long" "mutable" "new" "operator" "private" + "protected" "public" "reinterpret_cast" "short" "signed" "sizeof" "static_cast" + "template" "this" "throw" "try" "typename" "union" "unsigned" "using" "virtual")) + + (defvar hlsl-deprecated-qualifier-list + '()) + + (defvar hlsl-builtin-list + + '( + ;; This is the list of the builtins taken directly from the Direct3D 12 docs (Shader Model 6.0) + "abort" "abs" "acos" "all" "AllMemoryBarrier" "AllMemoryBarrierWithGroupSync" + "any" "asdouble" "asfloat" "asin" "asint" "asint" "asuint" "asuint" "atan" "atan2" + "ceil" "CheckAccessFullyMapped" "clamp" "clip" "cos" "cosh" "countbits" "cross" + "D3DCOLORtoUBYTE4" "ddx" "ddx_coarse" "ddx_fine" "ddy" "ddy_coarse" "ddy_fine" + "degrees" "determinant" "DeviceMemoryBarrier" "DeviceMemoryBarrierWithGroupSync" + "distance" "dot" "dst" "errorf" "EvaluateAttributeAtCentroid" "EvaluateAttributeAtSample" + "EvaluateAttributeSnapped" "exp" "exp2" "f16tof32" "f32tof16" "faceforward" + "firstbithigh" "firstbitlow" "floor" "fma" "fmod" "frac" "frexp" "fwidth" + "GetRenderTargetSampleCount" "GetRenderTargetSamplePosition" "GroupMemoryBarrier" + "GroupMemoryBarrierWithGroupSync" "InterlockedAdd" "InterlockedAnd" + "InterlockedCompareExchange" "InterlockedCompareStore" "InterlockedExchange" + "InterlockedMax" "InterlockedMin" "InterlockedOr" "InterlockedXor" "isfinite" + "isinf" "isnan" "ldexp" "length" "lerp" "lit" "log" "log10" "log2" "mad" "max" + "min" "modf" "msad4" "mul" "noise" "normalize" "pow" "printf" + "Process2DQuadTessFactorsAvg" "Process2DQuadTessFactorsMax" "Process2DQuadTessFactorsMin" + "ProcessIsolineTessFactors" "ProcessQuadTessFactorsAvg" "ProcessQuadTessFactorsMax" + "ProcessQuadTessFactorsMin" "ProcessTriTessFactorsAvg" "ProcessTriTessFactorsMax" + "ProcessTriTessFactorsMin" "radians" "rcp" "reflect" "refract" "reversebits" + "round" "rsqrt" "saturate" "sign" "sin" "sincos" "sinh" "smoothstep" "sqrt" + "step" "tan" "tanh" "tex1D" "tex1Dbias" "tex1Dgrad" "tex1Dlod" "tex1Dproj" + "tex2D" "tex2Dbias" "tex2Dgrad" "tex2Dlod" "tex2Dproj" "tex3D" "tex3Dbias" + "tex3Dgrad" "tex3Dlod" "tex3Dproj" "texCUBE" "texCUBEbias" "texCUBEgrad" "texCUBElod" + "texCUBEproj" "transpose" "trunc" + + ;; Geometry shader streams + "Append" "RestartStrip" + + ;; Textures/Buffers + "CalculateLevelOfDetail" "CalculateLevelOfDetailUnclamped" "Gather" "GetDimensions" + "GetSamplePosition" "Sample" "SampleBias" "SampleCmp" "SampleGrad" "SampleLevel" + "Operator\\[\\]" "Load[234]?" "Store[234]?" + "GatherRed" "GatherGreen" "GatherBlue" "GatherAlpha" "GatherCmp" "GatherCmpRed" + "GatherCmpGreen" "GatherCmpBlue" "GatherCmpAlpha" + "Sample" "SampleBias" "SampleCmp" "SampleCmpLevelZero" "SampleGrad" "SampleLevel" + + ;; Wave Intrinsics + "QuadReadAcrossDiagonal" "QuadReadLaneAt" "QuadReadAcrossX" "QuadReadAcrossY" + "WaveActiveAllEqual" "WaveActiveBitAnd" "WaveActiveBitOr" "WaveActiveBitXor" + "WaveActiveCountBits" "WaveActiveMax" "WaveActiveMin" "WaveActiveProduct" + "WaveActiveSum" "WaveActiveAllTrue" "WaveActiveAnyTrue" "WaveActiveBallot" + "WaveGetLaneCount" "WaveGetLaneIndex" "WaveIsFirstLane" "WavePrefixCountBits" + "WavePrefixProduct" "WavePrefixSum" "WaveReadLaneFirst" "WaveReadLaneAt" + + ;; Unsorted + "Consume" "DecrementCounter" "IncrementCounter")) + + (defvar hlsl-deprecated-builtin-list + '()) + + (defvar hlsl-deprecated-variables-list + '()) + + (defvar hlsl-preprocessor-directive-list + '( + "define" "elif" "else" "endif" "error" "if" "ifdef" "ifndef" "include" + "line" "pragma" "undef")) + + (defvar hlsl-preprocessor-expr-list + '( + ;; Taken directly from glsl-mode - not audited yet + "defined" "##")) + + (defvar hlsl-preprocessor-builtin-list + '( + ;; Taken directly from glsl-mode - not audited yet + "__LINE__" "__FILE__" "__VERSION__")) + + ;; TODO: Highlighting for annotations + + ) ; eval-and-compile + +(eval-and-compile + (defun hlsl-ppre (re) + ;; FIXME: This doesn't sanitise the inputs, so a bad member could corrupt the whole expression + (format "\\<\\(%s\\)\\>" (string-join re "\\|")))) + +(defvar hlsl-font-lock-keywords-1 + (append + (list + (cons (eval-when-compile + (format "^[ \t]*#[ \t]*\\<\\(%s\\)\\>" + (regexp-opt hlsl-preprocessor-directive-list))) + hlsl-preprocessor-face) + (cons (eval-when-compile + (hlsl-ppre hlsl-type-list)) + hlsl-type-face) + (cons (eval-when-compile + (hlsl-ppre hlsl-deprecated-qualifier-list)) + hlsl-deprecated-keyword-face) + (cons (eval-when-compile + (hlsl-ppre hlsl-reserved-list)) + hlsl-reserved-keyword-face) + (cons (eval-when-compile + (hlsl-ppre hlsl-qualifier-list)) + hlsl-qualifier-face) + (cons (eval-when-compile + (hlsl-ppre hlsl-keyword-list)) + hlsl-keyword-face) + (cons (eval-when-compile + (hlsl-ppre hlsl-preprocessor-builtin-list)) + hlsl-keyword-face) + (cons (eval-when-compile + (hlsl-ppre hlsl-deprecated-builtin-list)) + hlsl-deprecated-builtin-face) + (cons (eval-when-compile + (hlsl-ppre hlsl-builtin-list)) + hlsl-builtin-face) + (cons (eval-when-compile + (hlsl-ppre hlsl-deprecated-variables-list)) + hlsl-deprecated-variable-name-face) + ;; Swizzles are a special case. We want to highlight swizzles only when + ;; they're after a dot, but we don't want to highlight the dot itself. + (list (eval-when-compile + ;; TODO: Maybe these types of swizzles? + ;; /\.\(_m[0-3]\{2}\)\{1,4\}/ + ;; /\.\(_[1-4]\{2}\)\{1,4\}/ + "\\.\\_<\\([xyzw]\\{1,4\\}\\|[rgba]\\{1,4\\}\\)\\_>") + '(1 hlsl-builtin-face)) + ;; TODO: What to do about dedicated named variables? + ;; (cons "unity_[A-Z0-9a-z_]+" hlsl-variable-name-face) + ) + + (when hlsl-additional-types + (list + (cons (hlsl-ppre hlsl-additional-types) hlsl-type-face))) + (when hlsl-additional-keywords + (list + (cons (hlsl-ppre hlsl-additional-keywords) hlsl-keyword-face))) + (when hlsl-additional-qualifiers + (list + (cons (hlsl-ppre hlsl-additional-qualifiers) hlsl-qualifier-face))) + (when hlsl-additional-built-ins + (list + (cons (hlsl-ppre hlsl-additional-built-ins) hlsl-builtin-face))) + ) + "Highlighting expressions for HLSL mode.") + + +(defvar hlsl-font-lock-keywords hlsl-font-lock-keywords-1 + "Default highlighting expressions for HLSL mode.") + +(defvar hlsl-mode-syntax-table + (let ((hlsl-mode-syntax-table (make-syntax-table))) + (modify-syntax-entry ?/ ". 124b" hlsl-mode-syntax-table) + (modify-syntax-entry ?* ". 23" hlsl-mode-syntax-table) + (modify-syntax-entry ?\n "> b" hlsl-mode-syntax-table) + (modify-syntax-entry ?_ "w" hlsl-mode-syntax-table) + hlsl-mode-syntax-table) + "Syntax table for `hlsl-mode'.") + +(defvar hlsl-other-file-alist + ;; TODO: Add common pairings, e.g. vert & corresponding frag files, perhaps + ;; also geom + '() + "Alist of extensions to find given the current file's extension.") + +(defun hlsl-man-completion-list () + "Return list of all HLSL keywords." + (append hlsl-builtin-list hlsl-deprecated-builtin-list)) + +;; TODO: Switch over to HLSL docs? +;; (defun hlsl-find-man-page (thing) +;; "Collects and displays manual entry for HLSL built-in function THING." +;; (interactive +;; (let ((word (current-word nil t))) +;; (list +;; (completing-read +;; (concat "OpenGL.org HLSL man page: (" word "): ") +;; (hlsl-man-completion-list) +;; nil nil nil nil word)))) +;; (save-excursion +;; (apply hlsl-browse-url-function +;; (list (concat hlsl-man-pages-base-url thing ".xhtml"))))) + +;; TODO: Maybe remove easy menu? +(easy-menu-define hlsl-menu hlsl-mode-map + "HLSL Menu." + `("HLSL" + ["Comment Out Region" comment-region + (c-fn-region-is-active-p)] + ["Uncomment Region" (comment-region (region-beginning) + (region-end) '(4)) + (c-fn-region-is-active-p)] + ["Indent Expression" c-indent-exp + (memq (char-after) '(?\( ?\[ ?\{))] + ["Indent Line or Region" c-indent-line-or-region t] + ["Fill Comment Paragraph" c-fill-paragraph t] + "----" + ["Backward Statement" c-beginning-of-statement t] + ["Forward Statement" c-end-of-statement t] + "----" + ["Up Conditional" c-up-conditional t] + ["Backward Conditional" c-backward-conditional t] + ["Forward Conditional" c-forward-conditional t] + "----" + ["Backslashify" c-backslash-region (c-fn-region-is-active-p)] + "----" + ["Find HLSL Man Page" hlsl-find-man-page t] + )) + +;;;###autoload +(define-derived-mode hlsl-mode prog-mode "HLSL" + "Major mode for editing HLSL shader files." + (c-initialize-cc-mode t) + (setq abbrev-mode t) + (c-init-language-vars-for 'c-mode) + (c-common-init 'c-mode) + (cc-imenu-init cc-imenu-c++-generic-expression) + (set (make-local-variable 'font-lock-defaults) '(hlsl-font-lock-keywords)) + (set (make-local-variable 'ff-other-file-alist) 'hlsl-other-file-alist) + (set (make-local-variable 'comment-start) "// ") + (set (make-local-variable 'comment-end) "") + (set (make-local-variable 'comment-padding) "") + ;; TODO: Indentation rules for annotated for loops, e.g: + ;; [unroll] for (...) { + ;; --->| + ;; } + ;; Currently it just indents flat as though no scope was declared + (add-to-list 'align-c++-modes 'hlsl-mode) + (c-run-mode-hooks 'c-mode-common-hook) + (run-mode-hooks 'hlsl-mode-hook) + ;; TODO: Guard `c-make-noise-macro-regexps' based on Emacs version, then lower + ;; dependency. + :after-hook (progn (when (fboundp 'c-make-noise-macro-regexps) + ;; Depends on Emacs 26.1, guarding this allows us to + ;; support down to Emacs 24.4 + (c-make-noise-macro-regexps)) + (c-make-macro-with-semi-re) + (c-update-modeline))) + + +;; TODO: Float number highlighting (i.e. 1.0f) +;; TODO: Negative number highlighting +;; TODO: Highlight numbers adjacent to operators e.g. 1-variable + + +(provide 'hlsl-mode) +;;; hlsl-mode.el ends here diff --git a/local-lisp/move-text.el b/local-lisp/move-text.el new file mode 100644 index 0000000..87e02c9 --- /dev/null +++ b/local-lisp/move-text.el @@ -0,0 +1,197 @@ +;;; move-text.el --- Move current line or region with M-up or M-down. -*- lexical-binding: t; -*- + +;; filename: move-text.el +;; Description: Move current line or region with M-up or M-down. +;; Author: Jason Milkins +;; Keywords: edit +;; Url: https://github.com/emacsfodder/move-text +;; Compatibility: GNU Emacs 25.1 +;; Version: 2.0.10 +;; +;;; This file is NOT part of GNU Emacs + +;;; License +;; +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3, 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 General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program; see the file COPYING. If not, write to +;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth +;; Floor, Boston, MA 02110-1301, USA. + +;;; Commentary: +;; +;; MoveText 2.0.0 is a re-write of the old move-text and compatible with >= Emacs 25.1 +;; +;; It allows you to move the current line using M-up / M-down if a +;; region is marked, it will move the region instead. +;; +;; Using the prefix (C-u *number* or META *number*) you can predefine how +;; many lines move-text will travel. +;; + +;;; Installation: +;; +;; Put move-text.el to your load-path. +;; The load-path is usually ~/elisp/. +;; It's set in your ~/.emacs like this: +;; (add-to-list 'load-path (expand-file-name "~/elisp")) +;; +;; And the following to your ~/.emacs startup file. +;; +;; (require 'move-text) +;; (move-text-default-bindings) + +;;; Acknowledgements: +;; +;; Original v1.x was a Feature extracted from basic-edit-toolkit.el - by Andy Stewart (LazyCat) +;; + +;;; Code: +(require 'cl-lib) + +(defun move-text-get-region-and-prefix () + "Get the region and prefix for the `interactive' macro, without aborting. + +Note: `region-beginning' and `region-end' are the reason why an +`interactive' macro with \"r\" will blow up with the error: + +\"The mark is not set now, so there is no region\" + +We check with `use-region-p' to avoid calling +them when there's no region or it is not appropriate +to act on it. + +We use `prefix-numeric-value' to return a number. +" + (list + (when (use-region-p) (region-beginning)) ;; otherwise nil + (when (use-region-p) (region-end)) + (prefix-numeric-value current-prefix-arg))) + +;;;###autoload +(defun move-text--total-lines () + "Convenience function to get the total lines in the buffer / or narrowed buffer." + (line-number-at-pos (point-max))) + +;;;###autoload +(defun move-text--at-first-line-p () + "Predicate, is the point at the first line?" + (= (line-number-at-pos) 1)) + +;;;###autoload +(defun move-text--at-penultimate-line-p () + "Predicate, is the point at the penultimate line?" + (= (line-number-at-pos) (1- (move-text--total-lines)))) + +;; save-mark-and-excursion in Emacs 25 works like save-excursion did before +(eval-when-compile + (when (< emacs-major-version 25) + (defmacro save-mark-and-excursion (&rest body) + `(save-excursion ,@body)))) + +;;;###autoload +(defun move-text--last-line-is-just-newline () + "Predicate, is last line just a newline?" + (save-mark-and-excursion + (goto-char (point-max)) + (beginning-of-line) + (= (point-max) (point)))) + +;;;###autoload +(defun move-text--at-last-line-p () + "Predicate, is the point at the last line?" + (= (line-number-at-pos) (move-text--total-lines))) + +;;;###autoload +(defun move-text-line-up () + "Move the current line up." + (interactive) + (if (move-text--at-last-line-p) + (let ((target-point)) + (kill-whole-line) + (forward-line -1) + (beginning-of-line) + (setq target-point (point)) + (yank) + (unless (looking-at "\n") + (newline)) + (goto-char target-point)) + (let ((col (current-column))) + (progn (transpose-lines 1) + (forward-line -2) + (move-to-column col))))) + +;;;###autoload +(defun move-text-line-down () + "Move the current line down." + (interactive) + (unless (or + (move-text--at-last-line-p) + (and + (move-text--last-line-is-just-newline) + (move-text--at-penultimate-line-p))) + (let ((col (current-column))) + (forward-line 1) + (transpose-lines 1) + (forward-line -1) + (move-to-column col)))) + +;;;###autoload +(defun move-text-region (start end n) + "Move the current region (START END) up or down by N lines." + (interactive (move-text-get-region-and-prefix)) + (let ((line-text (delete-and-extract-region start end))) + (forward-line n) + (let ((start (point))) + (insert line-text) + (setq deactivate-mark nil) + (set-mark start)))) + +;;;###autoload +(defun move-text-region-up (start end n) + "Move the current region (START END) up by N lines." + (interactive (move-text-get-region-and-prefix)) + (move-text-region start end (- n))) + +;;;###autoload +(defun move-text-region-down (start end n) + "Move the current region (START END) down by N lines." + (interactive (move-text-get-region-and-prefix)) + (move-text-region start end n)) + +;;;###autoload +(defun move-text-up (start end n) + "Move the line or region (START END) up by N lines." + (interactive (move-text-get-region-and-prefix)) + (if (not (move-text--at-first-line-p)) + (if (region-active-p) + (move-text-region-up start end n) + (cl-loop repeat n do (move-text-line-up))))) + +;;;###autoload +(defun move-text-down (start end n) + "Move the line or region (START END) down by N lines." + (interactive (move-text-get-region-and-prefix)) + (if (region-active-p) + (move-text-region-down start end n) + (cl-loop repeat n do (move-text-line-down)))) + +;;;###autoload +(defun move-text-default-bindings () + "Bind `move-text-up' and `move-text-down' to M-up & M-down." + (interactive) + (global-set-key [M-down] 'move-text-down) + (global-set-key [M-up] 'move-text-up)) + +(provide 'move-text) + +;;; move-text.el ends here