321 lines
44 KiB
EmacsLisp
321 lines
44 KiB
EmacsLisp
|
;;; unityjs-mode.el --- Major mode for editing Unity JavaScript
|
||
|
|
||
|
;; Author: Juan Sebastian Muñoz <naruse@gmail.com>
|
||
|
|
||
|
;; Identation took from: http://cvs.savannah.gnu.org/viewvc/*checkout*/emacs/lisp/progmodes/js.el?root=emacs
|
||
|
;; Authors:
|
||
|
;; Karl Landstrom <karl.landstrom@brgeight.se>
|
||
|
;; Daniel Colascione <dan.colascione@gmail.com>
|
||
|
;;
|
||
|
;; Version: 1
|
||
|
;; Date: 2010-01-08
|
||
|
;; Keywords: Unity3D, Unity Javascript.
|
||
|
|
||
|
;; This file is licensed as GPL, 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 of the License, or
|
||
|
;; (at your option) any later version.
|
||
|
|
||
|
;; This file comes 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.
|
||
|
|
||
|
|
||
|
(defvar unityjs-mode-hook nil)
|
||
|
(defvar unityjs-mode-map
|
||
|
(let ((unityjs-mode-map (make-sparse-keymap)))
|
||
|
(define-key unityjs-mode-map "\C-j" 'newline-and-indent)
|
||
|
unityjs-mode-map)
|
||
|
"Keymap for UnityJS major mode")
|
||
|
|
||
|
(add-to-list 'auto-mode-alist '("\\.js\\'" . unityjs-mode))
|
||
|
|
||
|
;; Word Highlighting
|
||
|
|
||
|
(defconst unityjs-font-lock-keywords-1
|
||
|
(list
|
||
|
'("\\<\\(A\\(?:bs\\|c\\(?:ceptDrag\\|os\\)\\|dd\\(?:BinaryData\\|C\\(?:lip\\|o\\(?:lor\\|mponent\\(?:Menu\\)?\\|ntrol\\)\\|ursorRect\\)\\|DefaultControl\\|E\\(?:vent\\|xplosionForce\\)\\|F\\(?:ield\\|loat\\|orce\\(?:AtPosition\\)?\\)\\|Key\\|M\\(?:atrix\\|ixingTransform\\)\\|ObjectToAsset\\|Relative\\(?:\\(?:Forc\\|Torqu\\)e\\)\\|Torque\\|Vector\\)?\\|l\\(?:locateViewID\\|tDirectorySeparatorChar\\)\\|n\\(?:gle\\(?:Axis\\)?\\|i\\(?:mation\\(?:BlendMode\\(?:\\.\\(?:Additive\\|Blend\\)\\)?\\|C\\(?:lip\\(?:CurveData\\)?\\|urve\\)\\|Event\\|State\\|Utility\\)?\\|sotropicFiltering\\(?:\\.\\(?:\\(?:Dis\\|\\(?:Force\\)?En\\)able\\)\\)?\\)\\)\\|pp\\(?:l\\(?:ication\\|y\\(?:ModifiedProperties\\)?\\)\\|roximately\\)\\|rr\\(?:ay\\|owCap\\)\\|s\\(?:in\\|set\\(?:Bundle\\(?:Request\\)?\\|Database\\|Importer\\|P\\(?:athToGUID\\|ostprocessor\\)\\)\\|yncOperation\\)\\|tan2?\\|udio\\(?:Clip\\|Importer\\(?:Format\\(?:\\.\\(?:Compressed\\|Native\\)\\)?\\)?\\|Listener\\|Source\\|VelocityUpdateMode\\(?:\\.\\(?:Auto\\|Dynamic\\|Fixed\\)\\)?\\)\\|wake\\)\\|B\\(?:e\\(?:ep\\|gin\\(?:Area\\|G\\(?:UI\\|roup\\)\\|Horizontal\\|LayoutGroup\\|S\\(?:ample\\|crollView\\)\\|ToggleGroup\\|Vertical\\|Windows\\)?\\|haviour\\)\\|itStream\\|l\\(?:end\\|it\\(?:MultiTap\\)?\\)\\|o\\(?:neWeight\\|unds\\|x\\(?:Collider\\)?\\)\\|r\\(?:eak\\|ingWindowTo\\(?:Back\\|Front\\)\\|o\\(?:adcastMessage\\|wseURL\\)\\)\\|u\\(?:ild\\(?:AssetBundle\\(?:ExplicitAssetNames\\|Options\\(?:\\.\\(?:Co\\(?:\\(?:llectDependencie\\|mpleteAsset\\)s\\)\\|DeterministicAssetBundle\\)\\)?\\)?\\|Options\\(?:\\.\\(?:AutoRunPlayer\\|BuildAdditionalStreamedScenes\\|Development\\|ShowBuiltPlayer\\)\\)?\\|P\\(?:ipeline\\|layer\\)\\|Target\\(?:\\.\\(?:DashboardWidget\\|Standalone\\(?:OSX\\(?:Intel\\|PPC\\|Universal\\)\\|Windows\\)\\|WebPlayer\\(?:Streamed\\)?\\)\\)?\\)\\|tton\\)\\)\\|C\\(?:a\\(?:l\\(?:c\\(?:Height\\|LineTranslation\\|MinMaxWidth\\|S\\(?:\\(?:creenS\\)?ize\\)\\|ulate\\(?:FrustumPlanes\\|TransformPath\\)\\)\\|lbackFunction\\)\\|mera\\(?:ClearFlags\\(?:\\.\\(?:Depth\\|Nothing\\|S\\(?:kybox\\|olidColor\\)\\)\\)?\\)?\\|n\\(?:StreamedLevelBeLoaded\\|cel\\(?:Invoke\\|Quit\\)\\)\\|p\\(?:suleCollider\\|tureScreenshot\\)\\)\\|eil\\(?:ToInt\\)?\\|h\\(?:aracter\\(?:Controller\\|Joint\\)\\|eck\\(?:\\(?:Capsul\\|Spher\\)e\\)\\)\\|l\\(?:amp\\(?:01\\)?\\|ear\\(?:C\\(?:amera\\|urves\\)\\|HostList\\|Log\\|P\\(?:articles\\|rogressBar\\)\\|SnapshotTarget\\)?\\|o\\(?:neComponent\\|se\\(?:Connection\\|stPo\\(?:int\\(?:OnBounds\\|To\\(?:Arc\\|Disc\\|PolyLine\\)\\)\\|werOfTwo\\)\\)?\\)\\)\\|o\\(?:l\\(?:l\\(?:ectDe\\(?:epHierarchy\\|pendencies\\)\\|i\\(?:der\\|sion\\(?:Flags\\(?:\\.\\(?:Above\\|Below\\|None\\|Sides\\)\\)?\\)?\\)\\)\\|or\\(?:Field\\)?\\)\\|m\\(?:bine\\(?:Instance\\|Meshes\\)?\\|p\\(?:areTag\\|onent\\|ress\\(?:Texture\\)?\\)\\)\\|n\\(?:cat\\|eCap\\|figurableJoint\\(?:Motion\\(?:\\.\\(?:Free\\|L\\(?:\\(?:imit\\|ock\\)ed\\)\\)\\)?\\)?\\|nect\\(?:ionTesterStatus\\(?:\\.\\(?:Error\\|P\\(?:rivateIP\\(?:\\(?:HasNATPunchT\\|NoNATPuncht\\)hrough\\)\\|ublicIP\\(?:IsConnectable\\|\\(?:NoServerStart\\|PortBlock\\)ed\\)\\)\\|Undetermined\\)\\)?\\)?\\|stantForce\\|t\\(?:a\\(?:ctPoint\\|ins\\(?:Key\\|Value\\)?\\)\\|extMenu\\|rollerColliderHit\\)\\)\\|py\\(?:Asset\\|F\\(?:ileOrDirectory\\(?:FollowSymlinks\\)?\\|rom\\)\\|PropertiesFromMaterial\\|Serialized\\)?\\|routine\\|s\\|untRemaining\\)\\|r\\(?:eate\\(?:Asset\\|EmptyPrefab\\|GameObjectWithHideFlags\\|Instance\\|Primitive\\|\\(?:Snapsho\\|TerrainGameObjec\\)t\\)?\\|oss\\(?:Fade\\(?:Queued\\)?\\)?\\)\\|u\\(?:be\\(?:Cap\\|map\\(?:Face\\(?:\\.\\(?:Negative[XYZ]\\|Positive[XYZ]\\)\\)?\\)?\\)\\|stomEditor\\)\\|ylinderCap\\)\\|D\\(?:e\\(?:bug\\|creaseLevel\\|g2Rad\\|l\\(?:ete\\(?:A\\(?:ll\\|sset\\)\\|\\(?:FileOrDirector\\|Ke\\)y\\)\\|taAngle\\)\\|pthTextureMode\\(?:\\.\\(?:Depth\\(?:Normals\\)?\\|None\\)\\)?\\|stroy\\(?:Immediate\\|PlayerObjects\\)?\\|tachChildren\\)\\|i\\(?:rectorySeparatorChar\\|s\\(?:c\\(?:onnect\\)?\\|play\\(?:Dialog\\(?:Complex\\)?\\|P\\(?:opupMenu\\|rogressBar\\)\\|Wizard\\)\\|tance\\(?:PointLine\\|To\\(?:Arc\\|Circle\\|Disc\\|\\(?:
|
||
|
'("\\<\\(S\\(?:e\\(?:rializedPropertyType\\.\\(?:C\\(?:\\(?:haracte\\|olo\\)r\\)\\|Enum\\|Float\\|Integer\\|LayerMask\\|ObjectReference\\|Rect\\|String\\|Vector[23]\\)\\|t\\(?:A\\(?:ctiveRecursively\\|nimation\\(?:\\(?:Clip\\|Event\\)s\\)\\)\\|Bo\\(?:ol\\|rderColor\\)\\|C\\(?:amera\\|ol\\(?:ors?\\|umn\\)\\|urve\\)\\|D\\(?:\\(?:ensi\\|ir\\)ty\\)\\|EditorCurve\\|F\\(?:loat\\|romToRotation\\)\\|G\\(?:enericData\\|lobal\\(?:Color\\|Float\\|Matrix\\|ShaderProperty\\|Texture\\|Vector\\)\\)\\|Heights\\|Int\\|L\\(?:evelPrefix\\|ookRotation\\)\\|M\\(?:\\(?:atri\\|inMa\\)x\\)\\|N\\(?:ameSmart\\|e\\(?:ighbors\\|xtControlName\\)\\)\\|ObjectEnabled\\|P\\(?:ass\\|ixels?\\|osition\\)\\|R\\(?:e\\(?:ceivingEnabled\\|placementShader\\|solution\\|vertBackfacing\\)\\|ow\\)\\|S\\(?:cope\\|endingEnabled\\|napshotTarget\\|tring\\)\\|T\\(?:RS\\|exture\\(?:Offset\\|Scale\\)?\\|riangles\\)\\|Ve\\(?:ctor\\|rtexCount\\)\\|Width\\)\\)\\|h\\(?:ader\\|ift\\|ow\\(?:Help\\(?:ForObject\\|Page\\)\\|Notification\\|Utility\\)?\\)\\|i\\(?:gn\\|m\\(?:\\(?:pleMov\\|ulat\\)e\\)\\|n\\)\\|k\\(?:in\\(?:Quality\\(?:\\.\\(?:Auto\\|Bone[124]\\)\\)?\\|nedMeshRenderer\\)\\|ybox\\)\\|l\\(?:e\\(?:[er]p\\)\\|ider\\)\\|mooth\\(?:Damp\\(?:Angle\\)?\\|Step\\|Tangents\\)\\|o\\(?:\\(?:ftJointLimi\\|r\\)t\\)\\|p\\(?:ace\\(?:\\.\\(?:Self\\|World\\)\\)?\\|hereC\\(?:ap\\|ollider\\)\\|ringJoint\\)\\|qr\\(?:Distance\\|t\\)\\|t\\(?:art\\(?:A\\(?:nimationMode\\|ssetEditing\\)\\|Coroutine\\|Drag\\)?\\|ep\\|op\\(?:A\\(?:llCoroutines\\|nimationMode\\|ssetEditing\\)\\|Coroutine\\)?\\|ring\\)\\|upportsRenderTextureFormat\\|y\\(?:ncLayer\\|stem\\(?:Info\\|Language\\(?:\\.\\(?:A\\(?:frikaans\\|rabic\\)\\|B\\(?:asque\\|\\(?:elarus\\|ulgar\\)ian\\)\\|C\\(?:atalan\\|hinese\\|zech\\)\\|D\\(?:\\(?:anis\\|utc\\)h\\)\\|E\\(?:nglish\\|stonian\\)\\|F\\(?:aroese\\|\\(?:inni\\|ren\\)sh\\)\\|G\\(?:erman\\|reek\\)\\|H\\(?:ebrew\\|ugarian\\)\\|I\\(?:celandic\\|\\(?:ndones\\|tal\\)ian\\)\\|Japanese\\|Korean\\|L\\(?:\\(?:atv\\|ithuan\\)ian\\)\\|Norwegian\\|Po\\(?:lish\\|rtuguese\\)\\|R\\(?:\\(?:oman\\|uss\\)ian\\)\\|S\\(?:erboCroatian\\|lov\\(?:ak\\|enian\\)\\|\\(?:pan\\|wed\\)ish\\)\\|T\\(?:hai\\|urkish\\)\\|U\\(?:\\(?:krainia\\|nknow\\)n\\)\\|Vietnamese\\)\\)?\\)\\)\\)\\|T\\(?:R\\(?:IANGLE\\(?:S\\|_STRIP\\)\\|S\\)\\|a\\(?:gField\\|n\\)\\|e\\(?:rrain\\(?:Collider\\|Data\\|Lighting\\(?:\\.\\(?:Lightmap\\|Pixel\\|Vertex\\)\\)?\\)?\\|st\\(?:Connection\\(?:NAT\\)?\\|PlanesAABB\\)\\|x\\(?:Coord[23]?\\|t\\(?:A\\(?:lignment\\(?:\\.\\(?:Center\\|\\(?:Lef\\|Righ\\)t\\)\\)?\\|nchor\\(?:\\.\\(?:Lower\\(?:Center\\|\\(?:Lef\\|Righ\\)t\\)\\|Middle\\(?:Center\\|\\(?:Lef\\|Righ\\)t\\)\\|Upper\\(?:Center\\|\\(?:Lef\\|Righ\\)t\\)\\)\\)?\\|rea\\|sset\\)\\|Clipping\\(?:\\.\\(?:Clip\\|Overflow\\)\\)?\\|Field\\|Mesh\\|ure\\(?:2D\\|Format\\(?:\\.\\(?:A\\(?:RGB32\\|lpha8\\)\\|DXT[15]\\|PVRTC_RGB\\(?:A[24]\\|[24]\\)\\|RGB24\\)\\)?\\|Importer\\(?:Format\\(?:\\.\\(?:A\\(?:RGB\\(?:16\\|32\\)\\|lpha8\\|utomatic\\)\\|DXT[15]\\|PVRTC_RGB\\(?:A[24]\\|[24]\\)\\|RGB\\(?:16\\|24\\)\\)\\)?\\|GenerateCubemap\\(?:\\.\\(?:Cylindrical\\|N\\(?:iceSpheremap\\|one\\)\\|S\\(?:\\(?:impleS\\)?pheremap\\)\\)\\)?\\|MipFilter\\(?:\\.\\(?:\\(?:Box\\|Kaiser\\)Filter\\)\\)?\\|N\\(?:POTScale\\(?:\\.\\(?:None\\|To\\(?:Larger\\|Nearest\\|Smaller\\)\\)\\)?\\|ormalFilter\\(?:\\.S\\(?:obel\\|tandard\\)\\)?\\)\\)?\\|WrapMode\\(?:\\.\\(?:Clamp\\|Repeat\\)\\)?\\)?\\)\\)\\)\\|hreadPriority\\(?:\\.\\(?:BelowNormal\\|High\\|Low\\|Normal\\)\\)?\\|ime\\|o\\(?:AngleAxis\\|String\\|ggle\\|olbar\\)\\|r\\(?:a\\(?:ilRenderer\\|ns\\(?:form\\(?:Direction\\|Point\\)?\\|late\\)\\)\\|ueTypeFontImporter\\)\\)\\|U\\(?:n\\(?:EscapeURL\\|do\\|focusWindow\\|lo\\(?:ad\\(?:UnusedAssets\\)?\\|ckReloadAssemblies\\)\\|\\(?:registerHos\\|shif\\)t\\)\\|\\(?:pdat\\|s\\)e\\)\\|V\\(?:alidateMoveAsset\\|e\\(?:ctor\\(?:[234]Field\\|[234]\\)\\|rt\\(?:ex3?\\|icalS\\(?:\\(?:crollba\\|lide\\)r\\)\\)\\)\\|iewport\\(?:PointToRay\\|To\\(?:\\(?:Screen\\|World\\)Point\\)\\)?\\)\\|W\\(?:WW\\(?:Form\\)?\\|a\\(?:itFor\\(?:EndOfFrame\\|FixedUpdate\\|Seconds\\)\\|keUp\\)\\|heel\\(?:Collider\\|FrictionCurve
|
||
|
'("\\<\\(c\\(?:atch\\|lass\\)\\|else\\|f\\(?:or\\(?:each\\)?\\|unction\\)\\|i\\(?:f\\|mport\\)\\|new\\|p\\(?:rivate\\|ublic\\)\\|return\\|s\\(?:tatic\\|witch\\)\\|try\\|var\\|while\\)\\>" . font-lock-keyword-face))
|
||
|
"Minimal highlighting expressions for unityjs mode")
|
||
|
|
||
|
(defconst unityjs-font-lock-keywords-2
|
||
|
(append unityjs-font-lock-keywords-1
|
||
|
(list
|
||
|
'("\\<\\(Date\\|Number\\|Array\\|Object\\|Regex\\|b\\(oolean\\|yte\\)\\|char\\|double\\|float\\|int\\|long\\|s\\(byte\\|hort\\|tring\\)\\|u\\(long\\|\\(ni\\|shor\\)t\\)\\|void\\)\\>" . font-lock-type-face)
|
||
|
'("\\<\\(true\\|false\\|null\\)\\>" . font-lock-reference-face)))
|
||
|
"Additional Keywords to highlight in UnityJS mode")
|
||
|
|
||
|
(defvar unityjs-font-lock-keywords unityjs-font-lock-keywords-2
|
||
|
"Default highlighting expressions for UnityJS mode")
|
||
|
|
||
|
|
||
|
;;;;;;;;;;;;;;;;;
|
||
|
;; INDENTATION ;;
|
||
|
;;;;;;;;;;;;;;;;;
|
||
|
|
||
|
(defcustom js-indent-level 8
|
||
|
"Number of spaces for each indentation step in `js-mode'."
|
||
|
:type 'integer
|
||
|
:group 'js)
|
||
|
|
||
|
(defcustom js-expr-indent-offset 0
|
||
|
"Number of additional spaces used for indentation of continued expressions.
|
||
|
The value must be no less than minus `js-indent-level'."
|
||
|
:type 'integer
|
||
|
:group 'js)
|
||
|
|
||
|
|
||
|
(defun js--regexp-opt-symbol (list)
|
||
|
"Like `regexp-opt', but surround the result with `\\\\_<' and `\\\\_>'."
|
||
|
(concat "\\_<" (regexp-opt list t) "\\_>"))
|
||
|
|
||
|
|
||
|
(defcustom js-comment-lineup-func #'c-lineup-C-comments
|
||
|
"Lineup function for `cc-mode-style', for C comments in `js-mode'."
|
||
|
:type 'function
|
||
|
:group 'js)
|
||
|
|
||
|
(defun js--re-search-backward-inner (regexp &optional bound count)
|
||
|
"Auxiliary function for `js--re-search-backward'."
|
||
|
(let ((parse)
|
||
|
str-terminator
|
||
|
(orig-macro-start
|
||
|
(save-excursion
|
||
|
(and (js--beginning-of-macro)
|
||
|
(point)))))
|
||
|
(while (> count 0)
|
||
|
(re-search-backward regexp bound)
|
||
|
(when (and (> (point) (point-min))
|
||
|
(save-excursion (backward-char) (looking-at "/[/*]")))
|
||
|
(forward-char))
|
||
|
(setq parse (syntax-ppss))
|
||
|
(cond ((setq str-terminator (nth 3 parse))
|
||
|
(when (eq str-terminator t)
|
||
|
(setq str-terminator ?/))
|
||
|
(re-search-backward
|
||
|
(concat "\\([^\\]\\|^\\)" (string str-terminator))
|
||
|
(save-excursion (beginning-of-line) (point)) t))
|
||
|
((nth 7 parse)
|
||
|
(goto-char (nth 8 parse)))
|
||
|
((or (nth 4 parse)
|
||
|
(and (eq (char-before) ?/) (eq (char-after) ?*)))
|
||
|
(re-search-backward "/\\*"))
|
||
|
((and (not (and orig-macro-start
|
||
|
(>= (point) orig-macro-start)))
|
||
|
(js--beginning-of-macro)))
|
||
|
(t
|
||
|
(setq count (1- count))))))
|
||
|
(point))
|
||
|
|
||
|
|
||
|
(defconst js--opt-cpp-start "^\\s-*#\\s-*\\([[:alnum:]]+\\)"
|
||
|
"Regexp matching the prefix of a cpp directive.
|
||
|
This includes the directive name, or nil in languages without
|
||
|
preprocessor support. The first submatch surrounds the directive
|
||
|
name.")
|
||
|
|
||
|
(defun js--beginning-of-macro (&optional lim)
|
||
|
(let ((here (point)))
|
||
|
(save-restriction
|
||
|
(if lim (narrow-to-region lim (point-max)))
|
||
|
(beginning-of-line)
|
||
|
(while (eq (char-before (1- (point))) ?\\)
|
||
|
(forward-line -1))
|
||
|
(back-to-indentation)
|
||
|
(if (and (<= (point) here)
|
||
|
(looking-at js--opt-cpp-start))
|
||
|
t
|
||
|
(goto-char here)
|
||
|
nil))))
|
||
|
|
||
|
(defun js--re-search-backward (regexp &optional bound noerror count)
|
||
|
"Search backward, ignoring strings, preprocessor macros, and comments.
|
||
|
|
||
|
This function invokes `re-search-backward' but treats the buffer
|
||
|
as if strings, preprocessor macros, and comments have been
|
||
|
removed.
|
||
|
|
||
|
If invoked while inside a macro, treat the macro as normal text."
|
||
|
(let ((saved-point (point))
|
||
|
(search-expr
|
||
|
(cond ((null count)
|
||
|
'(js--re-search-backward-inner regexp bound 1))
|
||
|
((< count 0)
|
||
|
'(js--re-search-forward-inner regexp bound (- count)))
|
||
|
((> count 0)
|
||
|
'(js--re-search-backward-inner regexp bound count)))))
|
||
|
(condition-case err
|
||
|
(eval search-expr)
|
||
|
(search-failed
|
||
|
(goto-char saved-point)
|
||
|
(unless noerror
|
||
|
(error (error-message-string err)))))))
|
||
|
|
||
|
|
||
|
(defconst js--possibly-braceless-keyword-re
|
||
|
(js--regexp-opt-symbol
|
||
|
'("catch" "do" "else" "finally" "for" "if" "try" "while" "with"
|
||
|
"each"))
|
||
|
"Regexp matching keywords optionally followed by an opening brace.")
|
||
|
|
||
|
(defconst js--indent-operator-re
|
||
|
(concat "[-+*/%<>=&^|?:.]\\([^-+*/]\\|$\\)\\|"
|
||
|
(js--regexp-opt-symbol '("in" "instanceof")))
|
||
|
"Regexp matching operators that affect indentation of continued expressions.")
|
||
|
|
||
|
|
||
|
(defun js--looking-at-operator-p ()
|
||
|
"Return non-nil if point is on a JavaScript operator, other than a comma."
|
||
|
(save-match-data
|
||
|
(and (looking-at js--indent-operator-re)
|
||
|
(or (not (looking-at ":"))
|
||
|
(save-excursion
|
||
|
(and (js--re-search-backward "[?:{]\\|\\_<case\\_>" nil t)
|
||
|
(looking-at "?")))))))
|
||
|
|
||
|
|
||
|
(defun js--continued-expression-p ()
|
||
|
"Return non-nil if the current line continues an expression."
|
||
|
(save-excursion
|
||
|
(back-to-indentation)
|
||
|
(or (js--looking-at-operator-p)
|
||
|
(and (js--re-search-backward "\n" nil t)
|
||
|
(progn
|
||
|
(skip-chars-backward " \t")
|
||
|
(or (bobp) (backward-char))
|
||
|
(and (> (point) (point-min))
|
||
|
(save-excursion (backward-char) (not (looking-at "[/*]/")))
|
||
|
(js--looking-at-operator-p)
|
||
|
(and (progn (backward-char)
|
||
|
(not (looking-at "++\\|--\\|/[/*]"))))))))))
|
||
|
|
||
|
|
||
|
(defun js--end-of-do-while-loop-p ()
|
||
|
"Return non-nil if point is on the \"while\" of a do-while statement.
|
||
|
Otherwise, return nil. A braceless do-while statement spanning
|
||
|
several lines requires that the start of the loop is indented to
|
||
|
the same column as the current line."
|
||
|
(interactive)
|
||
|
(save-excursion
|
||
|
(save-match-data
|
||
|
(when (looking-at "\\s-*\\_<while\\_>")
|
||
|
(if (save-excursion
|
||
|
(skip-chars-backward "[ \t\n]*}")
|
||
|
(looking-at "[ \t\n]*}"))
|
||
|
(save-excursion
|
||
|
(backward-list) (forward-symbol -1) (looking-at "\\_<do\\_>"))
|
||
|
(js--re-search-backward "\\_<do\\_>" (point-at-bol) t)
|
||
|
(or (looking-at "\\_<do\\_>")
|
||
|
(let ((saved-indent (current-indentation)))
|
||
|
(while (and (js--re-search-backward "^\\s-*\\_<" nil t)
|
||
|
(/= (current-indentation) saved-indent)))
|
||
|
(and (looking-at "\\s-*\\_<do\\_>")
|
||
|
(not (js--re-search-forward
|
||
|
"\\_<while\\_>" (point-at-eol) t))
|
||
|
(= (current-indentation) saved-indent)))))))))
|
||
|
|
||
|
|
||
|
(defun js--ctrl-statement-indentation ()
|
||
|
"Helper function for `js--proper-indentation'.
|
||
|
Return the proper indentation of the current line if it starts
|
||
|
the body of a control statement without braces; otherwise, return
|
||
|
nil."
|
||
|
(save-excursion
|
||
|
(back-to-indentation)
|
||
|
(when (save-excursion
|
||
|
(and (not (eq (point-at-bol) (point-min)))
|
||
|
(not (looking-at "[{]"))
|
||
|
(progn
|
||
|
(js--re-search-backward "[[:graph:]]" nil t)
|
||
|
(or (eobp) (forward-char))
|
||
|
(when (= (char-before) ?\)) (backward-list))
|
||
|
(skip-syntax-backward " ")
|
||
|
(skip-syntax-backward "w_")
|
||
|
(looking-at js--possibly-braceless-keyword-re))
|
||
|
(not (js--end-of-do-while-loop-p))))
|
||
|
(save-excursion
|
||
|
(goto-char (match-beginning 0))
|
||
|
(+ (current-indentation) js-indent-level)))))
|
||
|
|
||
|
(defun js--get-c-offset (symbol anchor)
|
||
|
(let ((c-offsets-alist
|
||
|
(list (cons 'c js-comment-lineup-func))))
|
||
|
(c-get-syntactic-indentation (list (cons symbol anchor)))))
|
||
|
|
||
|
(defun js--proper-indentation (parse-status)
|
||
|
"Return the proper indentation for the current line."
|
||
|
(save-excursion
|
||
|
(back-to-indentation)
|
||
|
(cond ((nth 4 parse-status)
|
||
|
(js--get-c-offset 'c (nth 8 parse-status)))
|
||
|
((nth 8 parse-status) 0) ; inside string
|
||
|
((js--ctrl-statement-indentation))
|
||
|
((eq (char-after) ?#) 0)
|
||
|
((save-excursion (js--beginning-of-macro)) 4)
|
||
|
((nth 1 parse-status)
|
||
|
(let ((same-indent-p (looking-at
|
||
|
"[]})]\\|\\_<case\\_>\\|\\_<default\\_>"))
|
||
|
(continued-expr-p (js--continued-expression-p)))
|
||
|
(goto-char (nth 1 parse-status))
|
||
|
(if (looking-at "[({[]\\s-*\\(/[/*]\\|$\\)")
|
||
|
(progn
|
||
|
(skip-syntax-backward " ")
|
||
|
(when (eq (char-before) ?\)) (backward-list))
|
||
|
(back-to-indentation)
|
||
|
(cond (same-indent-p
|
||
|
(current-column))
|
||
|
(continued-expr-p
|
||
|
(+ (current-column) (* 2 js-indent-level)
|
||
|
js-expr-indent-offset))
|
||
|
(t
|
||
|
(+ (current-column) js-indent-level))))
|
||
|
(unless same-indent-p
|
||
|
(forward-char)
|
||
|
(skip-chars-forward " \t"))
|
||
|
(current-column))))
|
||
|
|
||
|
((js--continued-expression-p)
|
||
|
(+ js-indent-level js-expr-indent-offset))
|
||
|
(t 0))))
|
||
|
|
||
|
(defun js-indent-line ()
|
||
|
"Indent the current line as JavaScript."
|
||
|
(interactive)
|
||
|
(save-restriction
|
||
|
(widen)
|
||
|
(let* ((parse-status
|
||
|
(save-excursion (syntax-ppss (point-at-bol))))
|
||
|
(offset (- (current-column) (current-indentation))))
|
||
|
(indent-line-to (js--proper-indentation parse-status))
|
||
|
(when (> offset 0) (forward-char offset)))))
|
||
|
|
||
|
;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
;; INDENTATION ENDS ;;
|
||
|
;;;;;;;;;;;;;;;;;;;;;;;
|
||
|
|
||
|
;; Syntax table
|
||
|
(defvar unityjs-mode-syntax-table
|
||
|
(let ((unityjs-mode-syntax-table (make-syntax-table)))
|
||
|
(modify-syntax-entry ?_ "w" unityjs-mode-syntax-table)
|
||
|
(modify-syntax-entry ?/ ". 124b" unityjs-mode-syntax-table)
|
||
|
(modify-syntax-entry ?* ". 23" unityjs-mode-syntax-table)
|
||
|
(modify-syntax-entry ?\n "> b" unityjs-mode-syntax-table)
|
||
|
unityjs-mode-syntax-table)
|
||
|
"Syntax table for unityjs-mode")
|
||
|
|
||
|
|
||
|
(defun unityjs-mode ()
|
||
|
"Major mode for editing Unity Javascript files"
|
||
|
(interactive)
|
||
|
(kill-all-local-variables)
|
||
|
(set-syntax-table unityjs-mode-syntax-table)
|
||
|
(use-local-map unityjs-mode-map)
|
||
|
(set (make-local-variable 'font-lock-defaults) '(unityjs-font-lock-keywords))
|
||
|
(set (make-local-variable 'indent-line-function) 'js-indent-line)
|
||
|
(setq major-mode 'unityjs-mode)
|
||
|
(setq mode-name "unityjs-mode")
|
||
|
(run-hooks 'unityjs-mode-hook))
|
||
|
|
||
|
(provide 'unityjs-mode)
|