2175 lines
85 KiB
EmacsLisp
2175 lines
85 KiB
EmacsLisp
;; gas-mode.el --- mode for editing assembler code
|
|
|
|
;; Copyright (C) 2007 Heike C. Zimmerer
|
|
;; Time-stamp: <2007-12-27 18:14:08 hcz>
|
|
|
|
;; Author: Heike C. Zimmerer <hcz@hczim.de>
|
|
;; Created: 20 Feb 2007
|
|
;; Version: 1.10 2009-2-25 hcz
|
|
;; Keywords: languages
|
|
|
|
|
|
|
|
;; This file is written for GNU Emacs, and uses the same license
|
|
;; terms; however, it is an add-on and not part of it.
|
|
|
|
;; GNU Emacs 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 2, or (at your option)
|
|
;; any later version.
|
|
|
|
;; GNU Emacs 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 GNU Emacs; see the file COPYING. If not, write to the
|
|
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
;; Boston, MA 02110-1301, USA.
|
|
|
|
;;; Commentary:
|
|
|
|
;; To use this mode, put gas-mode.el somewhere on your load-path.
|
|
;; Then add this to your .emacs:
|
|
;;
|
|
;; (require 'gas-mode)
|
|
;; (add-to-list 'auto-mode-alist '("\\.S\\'" . gas-mode))
|
|
|
|
;; gas-mode recognizes gas syntax (including embedded C preprocessor
|
|
;; directives). It does a limited amount of parsing, so it can do
|
|
;; some fancy things with syntactic elements (like labels). It,
|
|
;; however does not know about the peculiarities of the special
|
|
;; processor you're writing code for (there are just too many of
|
|
;; them), so, for example, it can't tell register names from labels.
|
|
;; Also, its scope is limited to the file you are editing.
|
|
;;
|
|
;; Symbol highlighting: For a symbol to be recognized as such, it must
|
|
;; be in a field where (as per gas syntax) symbols can be placed
|
|
;; (i.e. label field, argument field, some directives). If point
|
|
;; rests on such a symbol and there are more of it in the current
|
|
;; buffer, it is highlighted and you can move forward and backward
|
|
;; between all places where that symbol is referenced or defined with
|
|
;; forward-sexp and backward-sexp.
|
|
;;
|
|
;; This is different from a simple string search in that only those
|
|
;; places are considered where the symbol is actually used. For local
|
|
;; labels, gas-mode resolves which references are associated with
|
|
;; which location and only highlights those that fit. The
|
|
;; highlighting is different for different types of symbols; see the
|
|
;; customization buffer for the gas-symbol-* faces for short
|
|
;; explanations of their meanings.
|
|
;;
|
|
;; Special forms of local labels (like `55$') are not (yet?)
|
|
;; supported.
|
|
|
|
;; A special feature may need some explanation: C passthroughs.
|
|
;; Assembler code is often used to write functions that are later
|
|
;; called by C programs. For this to work, you usually have to
|
|
;; maintain two files, one containing the assembler source, the other
|
|
;; holding the C interface declaration. C passthroughs allow you to
|
|
;; move the C declaration part into the assembler file next to the
|
|
;; function it belongs to.
|
|
;;
|
|
;; From the assembler's point of view, C passthroughs are just C syntax
|
|
;; comments with some small syntactic sugar added, like:
|
|
;;
|
|
;; /*C
|
|
;; int a_declaration(void);
|
|
;; extern volatile int another_declaration;
|
|
;;
|
|
;; /# and this will be passed as comment #/
|
|
;; */
|
|
;;
|
|
;; Note the `/*C' at the beginning and the `/# ... #/' for the nested
|
|
;; comment.
|
|
;;
|
|
;; It is then up to the Make process to generate a .h file, which
|
|
;; carries the declarations and the comment, changing the "/#" and "#/"
|
|
;; into "/*" and "*/".
|
|
;;
|
|
;; This may be done by including a line similar to the following into
|
|
;; your Makefile (assuming $(ASFILES) is a list of your assembly
|
|
;; language files) (and don't forget to use a TAB for the white space
|
|
;; which introduces the action lines (<TAB> echo ..., <TAB>sed -n ...):
|
|
;;
|
|
;; asm-C-defs.h: $(ASFILES)
|
|
;; echo '/* Definitions of assembly language functions */' > $@
|
|
;; echo '/* (automatically created by make) */' >> $@
|
|
;; sed -n '/[/][*]C/,/[*][/]/{s|/[*]C||;s|[*]/||;s|/#|/*|;s|#/|*/|;p}' \
|
|
;; $^ >> $@
|
|
;;
|
|
;; (Note the above code requires the "/*C" and "*/" to be on a line of
|
|
;; their own.) gas-mode recognizes this kind of comment by proper
|
|
;; syntax highlighting. Symbol highlighting is also supported. For a
|
|
;; symbol to be highlighted within C passthrough code, it must be
|
|
;; defined to be global (because only then it is visible to an
|
|
;; external C program) in the same buffer.
|
|
|
|
;;
|
|
;; This mode runs `gas-mode-hook' when initialization is complete.
|
|
;;
|
|
|
|
;; Bugs:
|
|
;;
|
|
;; Most probably, yes. You'll tell me (<hcz@hczim.de>).
|
|
;;
|
|
;; This code is *not tested at all* for syntaxes where
|
|
;; `gas-commant-char' differs from `?;'.
|
|
|
|
;;; Change Log:
|
|
;;
|
|
;; 2007-05-26 1st public release (hcz).
|
|
;;
|
|
;; 2007-05-30 docstrings, commentary,
|
|
;; open string recognition with gas-next-token/limit-re.
|
|
;;
|
|
;; 2007-12-05 Bug with Intel syntax in arguemnt field fixed
|
|
;;
|
|
;; 2007-12-25 Indents fixed for C comments without closing '*/'
|
|
;;
|
|
;;; Code:
|
|
|
|
(defgroup gas nil
|
|
"Mode for editing gas syntax assembler code."
|
|
:link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
|
|
:group 'languages)
|
|
|
|
(defcustom gas-comment-char ?\;
|
|
"The comment start character assumed by gas mode."
|
|
|
|
:type 'character
|
|
; :set 'gas-set-gas-comment-char
|
|
:group 'gas)
|
|
|
|
(defcustom gas-opcode-column 14
|
|
"The opcode column."
|
|
:type 'integer
|
|
:group 'gas)
|
|
|
|
(defcustom gas-argument-column 20
|
|
"The column for the arguments (if any)."
|
|
:type 'integer
|
|
:group 'gas)
|
|
|
|
(defcustom gas-comment-column 36
|
|
"The column where end of line asm comments go to."
|
|
:type 'integer
|
|
:group 'gas)
|
|
|
|
(defcustom gas-comment-char-starts-comment nil
|
|
"Always jump to comment column when a variable `gas-comment-char' is typed.
|
|
|
|
It t, starts/expands a comment if appropriate. When
|
|
unset (nil), use `gas-comment-dwim' (usually bound to M-;) to get there."
|
|
:type 'boolean
|
|
:group 'gas)
|
|
|
|
(defcustom gas-indent-current-field-only nil
|
|
"If nil, 'indent' indents all fileds on the current line.
|
|
Else only the current field is affected."
|
|
:type 'boolean
|
|
:group 'gas)
|
|
|
|
;; (defcustom gas-preserve-trailing-whitespace nil
|
|
;; "If nil, (re-)indenting removes trailing white space."
|
|
;; :type 'boolean
|
|
;; :group 'gas)
|
|
(setq gas-preserve-trailing-whitespace nil) ; (currently?) non-functional.
|
|
|
|
(defcustom gas-enable-symbol-highlight t
|
|
"Enable symbol recognition and highlighing.
|
|
|
|
When t and if point is on a symbol, some limited parsing data is
|
|
collected and all occurences of this symbol in the buffer get
|
|
highlighted according to the results (defined, global, etc.). As
|
|
long as a symbol is highlighted, `forward-sexp' and `backward-sexp'
|
|
move to the next/previous occurence of the same symbol in the
|
|
same buffer."
|
|
:type 'boolean
|
|
:group 'gas)
|
|
|
|
(defcustom gas-use-C-passthrough t
|
|
"When true, C passthrough comments are recognized.
|
|
|
|
This kind of comment is introduced by the starting sequence
|
|
\"/*C\" and is meant to be processed later by an external
|
|
program \(see the introducing comment in gas-mode.el for an
|
|
example) into C source code. Within these passthrough-comments,
|
|
the combination /# ... #/ is available for nested comments which
|
|
will be later changed into real C comments (/* ... */) by the
|
|
same external program."
|
|
:type 'boolean
|
|
:group 'gas)
|
|
|
|
(defcustom gas-defun-regexp "\n\\([;#].*\\|.*[*]/[ \t]*\\|\\|[ \t]*\\|[ \t]+\\..*\\)\n\\([^ \t\n;]+:\\)"
|
|
"Regexp used to recognize the beginning of a defun.
|
|
|
|
The default value describes a line which is either empty, a
|
|
full-line left-justified comment or a directive, followed by a
|
|
line starting with a label. Note that the character \";\" in the
|
|
regexp will be replaced by the actual comment character described
|
|
by variable `gas-comment-char'."
|
|
:type 'regexp
|
|
; :set 'gas-set-comment-regexp
|
|
:group 'gas)
|
|
|
|
|
|
|
|
(defcustom gas-defun-regexp-subexp 2
|
|
"The subexp in `gas-defun-regexp to jump to."
|
|
:type 'integer
|
|
:group 'gas)
|
|
|
|
(defcustom gas-C-indent 3
|
|
"Indent to use with C style comments."
|
|
:group 'gas
|
|
:type 'integer)
|
|
|
|
(defcustom gas-C-comment-end-column 0
|
|
"Where to indent a C comment end (\"*/\") if it starts a line."
|
|
:group 'gas
|
|
:type 'integer)
|
|
|
|
(defcustom gas-symbol-highlight-delay 0.5
|
|
"After this many seconds symbols get highlighted.
|
|
|
|
Number of seconds of idle time (a float) to wait before a symbol
|
|
gets highlighted."
|
|
:group 'gas
|
|
:type 'float)
|
|
|
|
(defgroup gas-faces nil
|
|
"Faces used by gas-mode."
|
|
:group 'gas)
|
|
|
|
(defface gas-builtin
|
|
'((((class color) (background light)) (:foreground "maroon"))
|
|
(t (:foreground "yellow")))
|
|
"Face to use for Gas buitins."
|
|
:group 'gas-faces)
|
|
|
|
(defface gas-symbol-ok
|
|
'((((class color) (background light)) (:background "#e0ffe0"))
|
|
(((class color) (background dark)) (:background "#001f00"))
|
|
(t (:foreground "yellow" :background "blue")))
|
|
"Face to use for symbols where exactly 1 definition was found."
|
|
:group 'gas-faces)
|
|
|
|
(defface gas-symbol-error
|
|
'((((class color) (background light))
|
|
(:background "#ffffe8" :foreground "red" :weight bold))
|
|
(((class color) (background dark))
|
|
(:background "#181800" :foreground "red" :weight bold))
|
|
(t (:foreground "yellow" :background "red")))
|
|
"Face to use when highlighting symbols with more than 1 definition."
|
|
:group 'gas-faces)
|
|
|
|
(defface gas-symbol-global
|
|
'((((class color) (background light)) (:background "#d0f8ff"))
|
|
(((class color) (background dark)) (:background "#00383f"))
|
|
(t (:foreground "yellow" :background "blue")))
|
|
"Face to use when highlighting global symbols."
|
|
:group 'gas-faces)
|
|
|
|
(defface gas-symbol-undef
|
|
'((((class color) (background light))
|
|
(:background "#ffffe8" :foreground "maroon"))
|
|
(((class color) (background dark))
|
|
(:background "#181800"))
|
|
(t (:foreground "yellow" :background "red")))
|
|
"Face to use for symbols defined as global when no definition
|
|
was found."
|
|
:group 'gas-faces)
|
|
|
|
(defface gas-symbol-global-undef
|
|
'((((class color) (background light))
|
|
(:background "#d0f8ff" :foreground "red"))
|
|
(((class color) (background dark))
|
|
(:background "#002840" :foreground "red"))
|
|
(t (:foreground "yellow" :background "red")))
|
|
"Face to use for symbols when no definition is found."
|
|
:group 'gas-faces)
|
|
|
|
(defface gas-passthrough-code
|
|
'((((class color) (background light)) (:foreground "magenta4"))
|
|
(((class color) (background dark)) (:foreground "magenta2"))
|
|
(t (:foreground "magenta1" :background "cyan")))
|
|
"Marks passthrough code."
|
|
:group 'gas-faces)
|
|
|
|
(defface gas-passthrough-comment
|
|
'((((class color) (background light)) (:foreground "turquoise4"))
|
|
(((class color) (background dark)) (:foreground "turquoise2"))
|
|
(t (:foreground "turquoise1")))
|
|
"Marks passthrough comments."
|
|
:group 'gas-faces)
|
|
|
|
(defvar gas-builtin-face 'gas-builtin)
|
|
(defvar gas-symbol-ok-face 'gas-symbol-ok)
|
|
(defvar gas-symbol-error-face 'gas-symbol-error)
|
|
(defvar gas-symbol-global-face 'gas-symbol-global)
|
|
(defvar gas-symbol-undef-face 'gas-symbol-undef)
|
|
(defvar gas-symbol-global-undef-face 'gas-symbol-global-undef)
|
|
(defvar gas-passthrough-code-face 'gas-passthrough-code)
|
|
(defvar gas-passthrough-comment-face 'gas-passthrough-comment)
|
|
|
|
(defconst gas-max-lines-in-cache 500
|
|
"Maximum number of parsed lines in cache.
|
|
|
|
I don't expect much impact from this on performance (the line
|
|
cache is emptied on any buffer change anyway). Play around with
|
|
this value if you suspect memory may be your problem.")
|
|
|
|
(defconst gas-max-labels-in-cache 300
|
|
"Maximum number of symbols in highlight cache.
|
|
Reduce this if memory footprint grows too high (very unlikely).")
|
|
|
|
(defconst gas-re-sym "\\([_$A-Za-z][_0-9$A-Za-z]*\\)"
|
|
"Regexp defining a valid symbol as a subexpression.")
|
|
|
|
(defconst gas-skip-sym "_0-9$A-Za-z"
|
|
"The valid characters for a symbol as used in `skip-chars-*' functions.")
|
|
|
|
(defconst gas-re-nosym"[^_0-9$A-Za-z]"
|
|
"Regexp defining the character set not allowed in a symbol.")
|
|
|
|
(defvar gas-mode-syntax-table
|
|
(let ((st (make-syntax-table)))
|
|
(modify-syntax-entry ?\n "> b" st)
|
|
(modify-syntax-entry ?. "_" st)
|
|
(modify-syntax-entry ?/ ". 124b" st)
|
|
(modify-syntax-entry ?* ". 23" st)
|
|
st)
|
|
"Syntax table used while in gas mode.")
|
|
|
|
(defvar gas-mode-abbrev-table nil
|
|
"Abbrev table used while in Gas mode.")
|
|
(define-abbrev-table 'gas-mode-abbrev-table ())
|
|
|
|
(defvar gas-mode-map
|
|
(let ((map (make-sparse-keymap)))
|
|
;; Note that the comment character isn't set up until gas-mode is called.
|
|
(define-key map ":" 'gas-colon)
|
|
(define-key map "\M-;" 'gas-comment)
|
|
(define-key map ";" 'gas-comment-char)
|
|
(define-key map "#" 'gas-hash)
|
|
(define-key map (kbd "<S-iso-lefttab>") 'gas-indent-backward)
|
|
(define-key map "\C-c;" 'comment-region)
|
|
(define-key map "\C-j" 'newline-and-indent)
|
|
(define-key map "\C-m" 'newline-and-indent)
|
|
map)
|
|
"Keymap for Gas mode.")
|
|
|
|
(defconst gas-equ (regexp-opt '(".equ" ".set" ".eqv" ".equiv" ".set"))
|
|
"Regex matching all operators which define a symbol.")
|
|
|
|
(defconst gas-indents
|
|
'((C-comment-end . gas-C-comment-end-column)
|
|
(C-comment-start . 0)
|
|
(C-comment . gas-get-C-relative-indent)
|
|
(cpp-macro-def . 0)
|
|
(cpp-argument . 0)
|
|
(label . 0)
|
|
(opcode . gas-opcode-column)
|
|
(argument . gas-argument-column)
|
|
(asm-comment . gas-get-asm-comment-column))
|
|
"Fields and their indents.
|
|
The cdr (the indent) may either be a number, a symbol bound to a
|
|
number, or a symbol bound to a function yielding the value.")
|
|
|
|
(defconst gas-parse-sequences
|
|
'(
|
|
(starting-asm-line
|
|
. ((cpp-macro-def . cpp-macro-def)
|
|
(label . label)
|
|
(empty-label . label)
|
|
(asm-comment . asm-comment)
|
|
(opcode . opcode)))
|
|
(label
|
|
. ((opcode . opcode)
|
|
(asm-comment . asm-comment)
|
|
(garbage . garbage)))
|
|
(opcode
|
|
. ((asm-comment . asm-comment)
|
|
(argument . argument)
|
|
(eol-ws . eol-ws)
|
|
(garbage . garbage)))
|
|
(argument
|
|
. ((asm-comment . asm-comment)
|
|
(eol-ws . eol-ws)
|
|
(garbage . garbage)))
|
|
(asm-comment
|
|
. ((eol-ws . eol-ws)))
|
|
(cpp-argument
|
|
. ((eol-ws . eol-ws)
|
|
(garbage . garbage)))
|
|
(cpp-macro-def
|
|
. ((cpp-argument . cpp-argument)
|
|
(garbage . garbage)))
|
|
(cpp-argument
|
|
. ((eol-ws)
|
|
(garbage . garbage)))
|
|
(C-comment-start
|
|
. ((C-comment . C-comment)))
|
|
(starting-within-C-comment
|
|
. ((C-comment-end . C-comment-end)
|
|
(C-comment . C-comment)))
|
|
(C-comment
|
|
. ((C-comment-end . C-comment-end)
|
|
(eol-ws . eol-ws)
|
|
(garbage . garbage)))
|
|
(garbage
|
|
. ((eol-ws . eol-ws)))
|
|
(eol-ws
|
|
. fini))
|
|
"Mapping from the field type we're on to the field types to check next.
|
|
car - type of field we're on (IOW, the one just handled)
|
|
cdr - ordered list of (first (most special) check first):
|
|
car - token to match
|
|
cdr - next field type iff match." )
|
|
|
|
(defconst gas-patterns
|
|
'((cpp-macro-def
|
|
"[ \t]*\\(\\(#[^ \t\n]*\\)\\)"
|
|
(0 1 1 1)
|
|
"/[*]")
|
|
(cpp-argument
|
|
"[ \t]*\\(\\([ \t]*\\([^ \t\n]\\)+\\)+\\)"
|
|
(0 1 1 1)
|
|
"/[*]")
|
|
(label
|
|
"[ \t]*\\([^ :\t\n]+:\\)"
|
|
(0 1 1 1)
|
|
"/[*]\\|;")
|
|
(opcode
|
|
"[ \t]*\\([^ \t\n]+\\)"
|
|
(0 1 1 1)
|
|
"/[*]\\|;")
|
|
(argument
|
|
"[ \t]*\\(\\([ \t]*\\([^ \t\n]\\)+\\)+\\)"
|
|
(0 1 1 1)
|
|
"/[*]\\|;")
|
|
(garbage
|
|
"[ \t]*\\(\\([ \t]*\\([^ \t\n]\\)+\\)+\\)"
|
|
(0 1 1 1)
|
|
"/[*]\\|;")
|
|
(asm-comment
|
|
"[ \t]*\\(\\(;+\\)\\([ \t]*[^ \t\n]+\\)*\\)"
|
|
(0 1 1 1)
|
|
nil)
|
|
(C-inline-comment
|
|
"[ \t]*\\(/[*].*?[*]/\\)"
|
|
(0 1 1 1)
|
|
nil)
|
|
(C-comment-start
|
|
"[ \t]*\\(/[*]C?\\)"
|
|
(0 1 1 1)
|
|
nil)
|
|
(empty-label
|
|
;; a field of at least 1 white space: no fill at start, nil
|
|
;; text field at start, fill at end
|
|
" [ \t]*"
|
|
(0 0 0 nil)
|
|
"/[*]\\|;")
|
|
(C-comment-end
|
|
"[ \t]*\\([*]/\\)"
|
|
(0 1 1 1)
|
|
nil)
|
|
(C-comment
|
|
;; match the entire line (except for trailing whitespace)
|
|
"[ \t]*\\(\\([ \t]*[^ \t]+\\)*\\)"
|
|
(0 1 1 1)
|
|
"[*]/")
|
|
;; fill only: zero or more white space, nil text field at
|
|
;; end
|
|
(eol-ws
|
|
"[ \t]+\\( ?\\)"
|
|
(0 1 1 1) ; last element always empty
|
|
nil))
|
|
"An alist of parse patterns.
|
|
|
|
Each entry holds 4 elements (SYMBOL REGEXP SUBEXPS TERMINATE-RE):
|
|
|
|
SYMBOL - designator (a symbol) under which it will be
|
|
referenced.
|
|
|
|
REGEXP - the regexp to match against,
|
|
|
|
SUBEXPS - a list (BEG-COL TEXT-COL END-COL END-OF-FIELD) of at
|
|
which subexpression of REGEXP to find beg-col, text-col,
|
|
end-col (see `gas-parse-line-really') and the end of the
|
|
field,
|
|
|
|
TERMINATE-RE - a regexp, the start of which (if it matches and
|
|
if outside a \"..\" string) unconditionally terminates the
|
|
field.
|
|
|
|
Every occurence of the character \";\" in both regexps is
|
|
replaced by variable `gas-comment-char' before use.")
|
|
|
|
(defconst gas-elmt-types
|
|
'(type subtype beg-col text-col end-col text modified)
|
|
"The elements of a gas syntax field.")
|
|
|
|
(defconst gas-builtin-keywords (concat "^\\(\\(\\sw\\|\\s_\\)+:?\\)?[ \t]+\\("
|
|
(regexp-opt '(
|
|
".Abort" ".ABORT" ".Align" ".Altmacro" ".Ascii" ".Asciz"
|
|
".Balign" ".Byte" ".Comm"
|
|
".Data" ".Def" ".Desc" ".Dim" ".Double" ".Eject"
|
|
".Else" ".Elseif" ".End" ".Endef" ".Endfunc" ".Endif"
|
|
".Equ" ".Equiv" ".Eqv"
|
|
".Err" ".Error" ".Exitm" ".Extern" ".Fail"
|
|
".File" ".Fill" ".Float" ".Func"
|
|
".Global" ".Hidden" ".hword" ".Ident"
|
|
".If" ".ifb" ".ifc" ".ifeq" ".ifeqs"
|
|
".ifge" ".ifle" ".ifgt" ".iflt"
|
|
".ifnb" ".ifnc" ".ifndef" ".ifdef" ".ifnotdef" ".ifne" ".ifnes"
|
|
".Incbin" ".Include" ".Int"
|
|
".Internal" ".Irp" ".Irpc" ".Lcomm" ".Lflags" ".Line"
|
|
".Linkonce" ".List" ".Ln"
|
|
".Long" ".Macro" ".MRI" ".Noaltmacro"
|
|
".Nolist" ".Octa" ".Org" ".P2align" ".PopSection" ".Previous"
|
|
".Print" ".Protected" ".Psize" ".Purgem" ".PushSection"
|
|
".Quad" ".Rept" ".Sbttl" ".Scl" ".Section" ".Set" ".Short"
|
|
".Single" ".Size" ".Skip" ".Sleb128" ".Space" ".Stab" ".String"
|
|
".Struct" ".SubSection" ".Symver" ".Tag" ".Text" ".Title" ".Type"
|
|
".Uleb128" ".Val" ".Version" ".VTableEntry" ".VTableInherit"
|
|
".Warning" ".Weak" ".Weakref" ".Word" ".Deprecated"))
|
|
"\\)[ \t\n]"))
|
|
|
|
(defvar gas-font-lock-keywords
|
|
(append
|
|
(list
|
|
'(gas-return-passthrough-code-hi . (0 gas-passthrough-code-face t))
|
|
'(gas-return-passthrough-comment-hi . (0 gas-passthrough-comment-face t))
|
|
'(gas-return-gas-hi-ok . (0 gas-symbol-ok-face t))
|
|
'(gas-return-gas-hi-global . (0 gas-symbol-global-face t))
|
|
'(gas-return-gas-hi-error . (0 gas-symbol-error-face t))
|
|
'(gas-return-gas-hi-undef . (0 gas-symbol-undef-face t))
|
|
'(gas-return-gas-hi-global-undef . (0 gas-symbol-global-undef-face t))
|
|
(list gas-builtin-keywords 3 'gas-builtin-face)
|
|
'("^\\(\\(\\sw\\|\\s_\\)+\\)\\>:?" 1 font-lock-function-name-face))
|
|
cpp-font-lock-keywords)
|
|
"Additional expressions to highlight in gas mode.")
|
|
(put 'gas-mode 'font-lock-defaults '(gas-font-lock-keywords))
|
|
|
|
;;;###autoload
|
|
|
|
(defun gas-mode ()
|
|
"Major mode for editing assembler code.
|
|
|
|
Commands:
|
|
|
|
\(Some of these commands may exhibit slightly different behaviour if point
|
|
is on a C syntax line.)
|
|
|
|
\\[indent-for-tab-command] indent the field(s) point is on. If it already is in its position,
|
|
move on to the next field on the line.
|
|
|
|
\\[gas-indent-backward] Move to the previous field.
|
|
|
|
\\[gas-comment] When no region is active, starts a comment sequence:
|
|
- If a comment is present and point is not at its start, jump there.
|
|
- Else start a comment. If there already is one, increase its comment level.
|
|
What that is and what it does, is best explained when you try
|
|
it out: Move to en empty line, then type \\[gas-comment] and
|
|
then repeatedly \\[gas-comment] or \\[gas-comment-char].
|
|
Calls \\[gas-comment-dwim] (see below) if the region is active.
|
|
|
|
\\[forward-sexp] If you're on a highlighted symbol, jump to its next
|
|
occurence. Else do `forward-sexp' like in text mode.
|
|
|
|
\\[backward-sexp] If you're on a highlighted symbol, jump to its previous
|
|
occurence. Else do `backward-sexp' like in text mode.
|
|
|
|
\\[gas-comment-dwim] If the region starts at the leading white space
|
|
before a comment, all full-line comments in region will be
|
|
removed. If the region starts on a comment, comments will be
|
|
removed, but comments with leading white space will be left
|
|
untouched. Else insert triple variable `gas-comment-char's before all
|
|
lines in region.
|
|
|
|
\\[fill-paragraph] beautifies the paragraph around
|
|
point, i.e. it adjusts all assembly syntax fields to their
|
|
standard positions.
|
|
|
|
\\[indent-region] beautifies the region, i.e., adjusts all fields in region.
|
|
|
|
|
|
The following characters have a special meaning in special cases:
|
|
|
|
\\[gas-colon] if it terminates a label: outdent the label and
|
|
move to opcode column. Else, just insert \\[gas-colon] as usual.
|
|
|
|
\\[gas-comment-char] The value which introduces an asm style comment.
|
|
If typed in in a row after \\[gas-comment], behaves as an alias to \\[gas-comment]. else
|
|
just insert \\[gas-comment-char].
|
|
Can be customized to always act as alias (`gas-comment-char-starts-comment').
|
|
|
|
Alternatively, you may use a File Variable to make it buffer local
|
|
(which allows you to use different syntaxes in the same session).
|
|
Note: Setting it to a value other than ?\; has not yet been tested.
|
|
|
|
\\[gas-hash] If it starts a preprocessor directive: Outdent it to first
|
|
column.
|
|
|
|
Customization: Entry on this mode runs `gas-mode-hook'.
|
|
The customization group is called 'gas'.
|
|
|
|
Special commands:
|
|
\\{gas-mode-map}"
|
|
(interactive)
|
|
(kill-all-local-variables)
|
|
(setq mode-name "gas")
|
|
(setq major-mode 'gas-mode)
|
|
(setq local-abbrev-table gas-mode-abbrev-table)
|
|
(setq gas-comment-string (string gas-comment-char))
|
|
(setq gas-comment-re (regexp-quote gas-comment-string))
|
|
(set (make-local-variable 'indent-line-function) 'gas-indent)
|
|
(set (make-local-variable 'indent-region-function) 'gas-indent-region)
|
|
(set (make-local-variable 'forward-sexp-function) 'gas-forward-sexp)
|
|
(set (make-local-variable 'fill-paragraph-function) 'gas-fill-paragraph)
|
|
(set (make-local-variable 'font-lock-defaults) '(gas-font-lock-keywords))
|
|
(set (make-local-variable 'gas-local-comment-char) gas-comment-char)
|
|
(set (make-local-variable 'beginning-of-defun-function)
|
|
'gas-beginning-of-defun)
|
|
(set (make-local-variable 'end-of-defun-function)
|
|
'gas-end-of-defun)
|
|
(set (make-local-variable 'font-lock-keywords-case-fold-search) t)
|
|
(use-local-map (nconc (make-sparse-keymap) gas-mode-map))
|
|
(local-set-key (vector gas-comment-char) 'gas-comment-char)
|
|
(set-syntax-table (make-syntax-table gas-mode-syntax-table))
|
|
(modify-syntax-entry gas-comment-char "< b")
|
|
(dolist (var '(gas-line-cache gas-globals-cache gas-locals-cache
|
|
gas-hi-valid gas-hi-sym-list gas-hi-global gas-hi-undef
|
|
gas-hi-error gas-hi-ok gas-changed gas-highlights gas-hi
|
|
gas-pass-code-hi gas-pass-comment-hi gas-symbol-timer
|
|
gas-highlights-error gas-symbol-highlight-beg
|
|
gas-symbol-highlight-end gas-doing-comment
|
|
after-change-functions))
|
|
(set (make-local-variable var) nil))
|
|
(add-to-list 'after-change-functions 'gas-after-change)
|
|
(add-hook 'pre-command-hook 'gas-symbol-pre-command)
|
|
(gas-start-symbol-timer)
|
|
(run-mode-hooks 'gas-mode-hook)
|
|
;; scan buffer for extra regions to highlight:
|
|
(gas-symbol-highlight))
|
|
|
|
(defun gas-dbg ()
|
|
"You won't need this unless you're debugging `gas-mode'."
|
|
(interactive)
|
|
(setq debug-on-error t)
|
|
;(setq debug-on-quit t)
|
|
(setq debug-items '(indent))
|
|
(switch-to-buffer-other-frame "*Messages*")
|
|
(switch-to-buffer-other-frame "*scratch*")
|
|
(info "Elisp")
|
|
(switch-to-buffer-other-frame "*scratch*")
|
|
(find-file-other-frame "x.S")
|
|
(switch-to-buffer-other-frame "x.S")
|
|
(switch-to-buffer-other-frame (get-buffer-create "*gas-dbg*"))
|
|
(find-file-other-frame "gas-mode.el")
|
|
(column-number-mode t)
|
|
(switch-to-buffer-other-frame "gas-mode.el"))
|
|
|
|
(defun dmsg (condition &rest args)
|
|
"Helper function, outputs debug messages into a buffer of their own.
|
|
|
|
If DEBUG-ITEMS (a symbol or a list) has a non-empty
|
|
intersection with CONDITION (a symbol or a list)', apply `format'
|
|
to ARGS and insert the result at the end of the buffer
|
|
`gas-dbg' (which is created if non-existing).
|
|
|
|
Currently defined symbols are: 'wip (work in progress),
|
|
'hi (highlighting), 'parser, 'cursor, 'sym (symbol highlighting),
|
|
'cursor."
|
|
(when (and (boundp 'debug-items) debug-items)
|
|
(when (not (listp condition))
|
|
(setq condition (list condition)))
|
|
(when (not (listp debug-items))
|
|
(setq debug-items (list debug-items)))
|
|
(when (or (eq condition '(all))
|
|
(catch 'found
|
|
(dolist (c condition)
|
|
(when (member c debug-items)
|
|
(throw 'found t)))))
|
|
(let ((contents (apply 'format args)))
|
|
(save-current-buffer
|
|
(set-buffer (get-buffer-create "*gas-dbg*"))
|
|
(goto-char (point-max))
|
|
(newline)
|
|
(insert contents)
|
|
(goto-char (point-max))
|
|
;(recenter -1)
|
|
)))))
|
|
|
|
(defun gas-change-comment-regexp (str)
|
|
"Return STR with all \";\"s replaced by (regexp-quote variable `gas-comment-char')."
|
|
(when str
|
|
(setq str (replace-regexp-in-string
|
|
".*\\[[^]]*\\(;\\)" gas-comment-string str t t 1))
|
|
(replace-regexp-in-string ";" (regexp-quote gas-comment-string) str t t)))
|
|
|
|
(defun gas-change-comment-string (str)
|
|
"Return STR with all \";\"s replaced by variable `gas-comment-char'."
|
|
(when str
|
|
(replace-regexp-in-string ";" gas-comment-string str t t)))
|
|
|
|
(defun gas-set-patterns (comment-char)
|
|
"Replace `gas-patterns' by a copy, replacing ?\; by COMMENT-CHAR.
|
|
Also sets variable `gas-comment-char' to COMMENT-CHAR."
|
|
(set (make-local-variable 'gas-comment-char) comment-char)
|
|
(set (make-local-variable 'gas-comment-string) (string gas-comment-char))
|
|
(set (make-local-variable 'gas-comment-re) (regexp-quote gas-comment-string))
|
|
(kill-local-variable 'gas-patterns)
|
|
(let (result)
|
|
(dolist (pattern gas-patterns)
|
|
(add-to-list 'result
|
|
(list
|
|
(car pattern)
|
|
(gas-change-comment-regexp (nth 1 pattern))
|
|
(nth 2 pattern)
|
|
(gas-change-comment-regexp (nth 3 pattern)))))
|
|
(set (make-local-variable 'gas-patterns) result)
|
|
(dolist (sym '(gas-defun-regexp))
|
|
(kill-local-variable sym)
|
|
(let ((result (gas-change-comment-regexp (eval sym))))
|
|
(set (make-local-variable sym) result)))))
|
|
|
|
(defun gas-after-change (beg end len)
|
|
"Invalidate saved parser state.
|
|
Argument BEG BEG, END, and LEN, athough saved in `gas-changed' for debugging purposes, are not used."
|
|
(setq gas-globals-cache nil)
|
|
(setq gas-hi-valid nil)
|
|
(setq gas-locals-cache nil)
|
|
(setq gas-changed (append gas-changed (list (current-buffer) beg end len))))
|
|
|
|
(defun gas-symbol-pre-command()
|
|
(setq gas-hi nil))
|
|
|
|
(defun gas-start-symbol-timer (&optional stop)
|
|
"Schedule a timer for symbol highlighting (if not already scheduled).
|
|
Optional STOP, if non-nil, means remove from schedule."
|
|
(if (and stop gas-symbol-timer)
|
|
(progn
|
|
(cancel-timer gas-symbol-timer)
|
|
(setq gas-symbol-timer nil))
|
|
(unless (and gas-symbol-timer
|
|
(memq gas-symbol-timer timer-idle-list))
|
|
(setq gas-symbol-timer
|
|
(run-with-idle-timer gas-symbol-highlight-delay
|
|
t
|
|
'gas-symbol-highlight-maybe)))))
|
|
|
|
(defun gas-return-passthrough-hi (pos what)
|
|
"Return next passthrough match (if any).
|
|
See highlight.el for POS and WHAT."
|
|
(let ((curpoint (point)))
|
|
(catch 'found
|
|
(dolist (match what)
|
|
(when (and (< curpoint (cdr match))
|
|
(> pos (car match)))
|
|
(when (< curpoint (car match))
|
|
(goto-char (car match)))
|
|
(when (re-search-forward ".+" (min pos (cdr match)) t)
|
|
(dmsg 'hi "match: %s, data: '%s'" match (match-string 0))
|
|
(throw 'found t)))
|
|
(when (>= (car match) pos)
|
|
(throw 'found nil))))))
|
|
|
|
(defun gas-return-passthrough-code-hi (pos)
|
|
"Return next passthrough code match (if any).
|
|
See highlight.el for documentation on POS."
|
|
(gas-return-passthrough-hi pos gas-pass-code-hi))
|
|
|
|
(defun gas-return-passthrough-comment-hi (pos)
|
|
"Return next passthrough comment match (if any).
|
|
See highlight.el for documentaion on POS ."
|
|
(gas-return-passthrough-hi pos gas-pass-comment-hi))
|
|
|
|
(defun gas-passthrough-highlight ()
|
|
"Compute blocks of C passthroughs to be highlighted."
|
|
(goto-char (point-min))
|
|
(setq gas-pass-code-hi nil)
|
|
(setq gas-pass-comment-hi nil)
|
|
(let (beg end limit)
|
|
(while (and (re-search-forward "/[*]C" nil t)
|
|
(setq beg (point))
|
|
(setq limit
|
|
(save-excursion
|
|
(and (re-search-forward "[*]/" nil t)
|
|
(- (point) 2)))))
|
|
(while (and beg
|
|
(setq end (and (re-search-forward "/#" limit t)
|
|
(point))))
|
|
(add-to-list 'gas-pass-code-hi (cons beg (- end 2)) t)
|
|
(forward-char 2)
|
|
(when
|
|
(setq beg (and (re-search-forward "#/" limit t) (point)))
|
|
(add-to-list 'gas-pass-comment-hi (cons (- end 2) beg) t)))
|
|
(when beg
|
|
(add-to-list 'gas-pass-code-hi (cons beg limit) t)))))
|
|
|
|
(defun gas-return-highlight (pos hi-list)
|
|
"Called through the gas-return-gas-hi-* functions by highlight.el.
|
|
If there's a match of POS against one of the entries in HI-LIST,
|
|
return match data. Else, return nil."
|
|
(when gas-hi-valid
|
|
(dmsg 'hi "gas-return-highlight, pos: %s, point: %s, list: %s" pos (point) hi-list)
|
|
(catch 'found
|
|
(dolist (match hi-list)
|
|
(if (< (car match) pos)
|
|
(when (>= (car match) (point))
|
|
(dmsg 'hi "gas-return-highlight, match: %s" match)
|
|
(goto-char (car match))
|
|
(throw 'found (re-search-forward ".+" (min pos (cadr match)) t)))
|
|
(dmsg 'hi "gas-return-highlight: nope.")
|
|
(throw 'found nil)
|
|
nil)))))
|
|
|
|
(defun gas-return-gas-hi-global (pos)
|
|
"Check POS against the entries in the list `gas-hi-global'.
|
|
Called by highlight.el."
|
|
(gas-return-highlight pos gas-hi-global))
|
|
|
|
(defun gas-return-gas-hi-undef (pos)
|
|
"Check POS against the entries in the list `gas-hi-undef'.
|
|
Called by highlight.el."
|
|
(gas-return-highlight pos gas-hi-undef))
|
|
|
|
(defun gas-return-gas-hi-error (pos)
|
|
"Check POS against the entries in the list `gas-hi-error'.
|
|
Called by highlight.el."
|
|
(gas-return-highlight pos gas-hi-error))
|
|
|
|
(defun gas-return-gas-hi-ok (pos)
|
|
"Check POS against the entries in the list `gas-hi-ok'.
|
|
Called by highlight.el."
|
|
(gas-return-highlight pos gas-hi-ok))
|
|
|
|
(defun gas-return-gas-hi-global-undef (pos)
|
|
"Check POS against the entries in the list `gas-hi-undef'.
|
|
Called by highlight.el."
|
|
(gas-return-highlight pos gas-hi-global-undef))
|
|
|
|
|
|
(defun gas-qualify-symbol (sym-re slist lflags)
|
|
"The common part of `gas-scan-global-symbol and `gas-scan-local-symbol'.
|
|
|
|
Gets called with point on a line where a label match may be
|
|
found. It expects SYM-RE to be a regexp describing the
|
|
label. Adds on match the match it finds to SLIST (a symbol bound
|
|
to a list of matches) and the kind of match (as a symbol, like
|
|
'def for a definition or 'duplicate for a duplicate definition)
|
|
to LFLAGS."
|
|
(save-excursion
|
|
(let ((eol (line-end-position))
|
|
(bol (line-beginning-position)))
|
|
(setq case-fold-search nil)
|
|
(if (gas-C-comment-p)
|
|
(progn
|
|
(backward-char)
|
|
(while (re-search-forward
|
|
(format "%s\\(%s\\)\\(%s\\|$\\)"
|
|
gas-re-nosym sym-re gas-re-nosym)
|
|
eol t)
|
|
(let ((beg (match-beginning 1))
|
|
(end (match-end 1)))
|
|
(when (gas-C-passthrough-code-p)
|
|
(add-to-list lflags 'C-ref t)
|
|
(add-to-list slist (list 'C-ref beg end) t)))))
|
|
;; not C style:
|
|
(let* ((fields (gas-parsed))
|
|
(lbl (gas-nth 'text 'label fields))
|
|
(arg (gas-nth 'text 'argument fields))
|
|
(type 'ref)
|
|
(argno 0))
|
|
(when (and lbl
|
|
(string-match (concat "^" sym-re ":?$") lbl))
|
|
(setq nlabels (1+ nlabels))
|
|
(if (member 'def (eval lflags))
|
|
(add-to-list lflags 'duplicate t)
|
|
(add-to-list lflags 'def t))
|
|
(move-to-column 0)
|
|
(looking-at "[^:]+:")
|
|
(add-to-list slist
|
|
(list 'def (match-beginning 0) (match-end 0)) t))
|
|
(when arg
|
|
(move-to-column (- (gas-nth 'text-col 'argument fields) 1))
|
|
(let ((type 'ref)
|
|
(eo-arg (+ bol (gas-nth 'end-col 'argument fields) 2))
|
|
(opcode (gas-nth 'text 'opcode fields)))
|
|
(while (re-search-forward
|
|
(format "%s\\(%s\\)\\(%s\\|$\\)"
|
|
gas-re-nosym sym-re gas-re-nosym)
|
|
eo-arg t)
|
|
(let ((beg (match-beginning 1))
|
|
(end (match-end 1)))
|
|
;; first argument?
|
|
(if (and (= argno 0) ; yes
|
|
(string-match gas-equ opcode)) ; assignment?
|
|
(progn
|
|
(setq type 'def) ; yes
|
|
(if (member 'def (eval lflags))
|
|
(add-to-list lflags 'duplicate t)
|
|
(add-to-list lflags 'def t)))
|
|
(add-to-list lflags 'ref t)) ; no
|
|
(when (string-match ".global" opcode)
|
|
(add-to-list lflags 'global t))
|
|
(add-to-list slist (list type beg end) t)
|
|
(setq argno (1+ argno))))))))))
|
|
;; skip past parsed part:
|
|
(end-of-line))
|
|
|
|
(defun gas-scan-global-symbol (sym)
|
|
"Scan the buffer vor valid occurences of the global symbol SYM.
|
|
Called by `gas-symbol-highlight'."
|
|
(goto-char (point-min))
|
|
(let* (sym-list
|
|
flags
|
|
qualifiers
|
|
(nlabels 0)
|
|
(sym-re (regexp-quote sym))
|
|
(re (format "\\(^\\|%s\\)\\(%s\\)\\(%s\\|$\\)"
|
|
gas-re-nosym sym-re gas-re-nosym)))
|
|
(while (re-search-forward re nil t)
|
|
(when (input-pending-p)
|
|
(throw 'event-abort nil))
|
|
(goto-char (match-beginning 2))
|
|
(dmsg 'sym "global: qualified1: %s" sym-list)
|
|
(gas-qualify-symbol sym-re 'sym-list 'flags))
|
|
(dmsg 'sym "global: sym-list: %s" sym-list)
|
|
(unless (equal sym-list '(nil))
|
|
(list sym flags sym-list))))
|
|
|
|
(defun gas-scan-local-symbol (orig-sym)
|
|
"Scan the buffer vor valid occurences of the local symbol SYM.
|
|
Called by `gas-symbol-highlight'.
|
|
Argument ORIG-SYM is the complete symbol (as written)."
|
|
(when (string-match "^\\(.*\\)\\([:bf]\\)" orig-sym)
|
|
(let* ((search-lo (point-min)) ; location of previous duplicate
|
|
search-mid ; label pos
|
|
(search-hi (point-max)) ; location of next duplicate
|
|
(sym (match-string 1 orig-sym))
|
|
(sym-kind (match-string 2 orig-sym))
|
|
(sym-re (regexp-quote sym))
|
|
(lbl-re (concat "^\\(" sym-re ":" "\\)" ))
|
|
(nlabels 0)
|
|
sym-list
|
|
flags
|
|
qualifiers
|
|
searches)
|
|
;; determine region where the label is valid
|
|
(save-excursion
|
|
(when (equal sym-kind "b")
|
|
(re-search-backward lbl-re nil t)) ; skip label
|
|
(when (re-search-backward lbl-re nil t)
|
|
(setq search-lo (match-end 1)))
|
|
(goto-char search-lo)
|
|
(when (re-search-forward lbl-re nil t)
|
|
(setq search-mid (match-beginning 1)))
|
|
(when (re-search-forward lbl-re nil t)
|
|
(setq search-hi (match-beginning 1))))
|
|
(let ((lo search-hi) ; first match
|
|
(hi search-lo) ; end of last match
|
|
(search-params
|
|
(if search-mid
|
|
(list
|
|
(list (concat sym-re "f") search-lo search-mid)
|
|
(list (concat sym-re ":")
|
|
search-mid (+ search-mid (length sym) 2))
|
|
(list (concat sym-re "b") search-mid search-hi))
|
|
(list
|
|
(list (concat sym-re "f") search-lo search-hi)
|
|
(list (concat sym-re "b") search-lo search-hi)))))
|
|
(dolist (param search-params)
|
|
(when (input-pending-p)
|
|
(throw 'event-abort nil))
|
|
(let* ((sym-re (car param))
|
|
(search-re (concat gas-re-nosym
|
|
"\\(" sym-re "\\)" gas-re-nosym))
|
|
(limit (caddr param)))
|
|
(goto-char (- (cadr param) 1))
|
|
(while (and (< (point) limit)
|
|
(re-search-forward search-re limit t))
|
|
(setq lo (min lo (match-beginning 1)))
|
|
(setq hi (max hi (match-end 1)))
|
|
(goto-char (match-beginning 1))
|
|
(gas-qualify-symbol sym-re 'sym-list 'flags))))
|
|
(dmsg 'sym "local: sym-list: %s" sym-list)
|
|
(unless (equal sym-list '(nil))
|
|
(list sym lo hi flags sym-list))))))
|
|
|
|
(defun gas-sym-invalidate ()
|
|
"Invalidate all symbol scan results."
|
|
(setq gas-hi-sym-list nil)
|
|
(setq gas-hi-global nil)
|
|
(setq gas-hi-global-undef nil)
|
|
(setq gas-hi-undef nil)
|
|
(setq gas-hi-error nil)
|
|
(setq gas-hi-ok nil))
|
|
|
|
(defun gas-symbol-highlight ()
|
|
"Get symbol point is on, look for match and highlight it.
|
|
For use with the idle timer."
|
|
;; To debug this part, stop the idle timer mechanism first:
|
|
;; (gas-start-symbol-timer 'stop)
|
|
;; then eval it.
|
|
(save-match-data
|
|
(save-excursion
|
|
(gas-passthrough-highlight))
|
|
(save-excursion
|
|
(let* ((fields (gas-parsed))
|
|
(curpoint (point))
|
|
(case-fold-orig case-fold-search)
|
|
(pointpos (gas-get-pointpos fields))
|
|
sym-list)
|
|
(catch 'event-abort
|
|
(dmsg 'hi "pointpos=%s, fields=%s" pointpos fields)
|
|
;; skip to the symbol's start
|
|
(skip-chars-backward gas-skip-sym)
|
|
(when (member (car pointpos) '(label argument C-comment))
|
|
(setq case-fold-search nil)
|
|
(setq sym-list
|
|
(catch 'found
|
|
;; local label?
|
|
(if (and (not (eq (car pointpos) 'C-comment))
|
|
(looking-at (concat "\\(\\([0-9]+\\)\\([bf:]\\)\\)"
|
|
gas-re-nosym)))
|
|
(let ((lbl (match-string 2)))
|
|
;; local label
|
|
(dolist (entry gas-locals-cache)
|
|
(when (input-pending-p)
|
|
(throw 'event-abort nil))
|
|
(and (equal (car entry) lbl)
|
|
(>= (nth 1 entry) curpoint)
|
|
(<= (nth 2 entry) curpoint)
|
|
(dolist (sym (nthcdr 3 entry))
|
|
(and (>= (nth 1 entry) curpoint)
|
|
(<= (nth 2 entry) curpoint)
|
|
(throw 'found (nthcdr 3 entry)))))
|
|
nil)
|
|
;; not found in cache
|
|
(let ((entry (gas-scan-local-symbol (match-string 1))))
|
|
(when entry
|
|
(when (> (length gas-locals-cache)
|
|
gas-max-labels-in-cache)
|
|
(dmsg 'sym "gas-locals-cache truncated.")
|
|
(nbutlast gas-locals-cache
|
|
(/ (* gas-max-labels-in-cache 3) 4)))
|
|
(add-to-list 'gas-locals-cache entry)
|
|
(throw 'found (nthcdr 3 entry)))))
|
|
;; global label:
|
|
(when (looking-at (format "\\(%s+\\)%s"
|
|
gas-re-sym gas-re-nosym))
|
|
(setq lbl (match-string 1))
|
|
(when (setq entry (assoc lbl gas-globals-cache))
|
|
(throw 'found (cdr entry)))
|
|
;; not found in cache
|
|
(let ((entry (gas-scan-global-symbol lbl)))
|
|
(when entry
|
|
(when (> (length gas-globals-cache)
|
|
gas-max-labels-in-cache)
|
|
(dmsg 'sym "gas-globals-cache truncated.")
|
|
(nbutlast gas-globals-cache
|
|
(/ (* gas-max-labels-in-cache 3) 4)))
|
|
(add-to-list 'gas-globals-cache entry)
|
|
(throw 'found (cdr entry))))))))
|
|
|
|
(dmsg 'sym "gas-symbol-highlight: Matched: %s" sym-list)
|
|
(gas-sym-invalidate)
|
|
(setq gas-hi-sym-list sym-list)
|
|
(let ((flags (car sym-list))
|
|
target-list)
|
|
(dolist (sym (cadr sym-list))
|
|
(when (input-pending-p)
|
|
(throw 'event-abort nil))
|
|
(setq target-list
|
|
(cond
|
|
((member 'duplicate flags) 'gas-hi-error)
|
|
((and (member 'global flags) (member 'def flags))
|
|
'gas-hi-global)
|
|
((member 'global flags) 'gas-hi-global-undef)
|
|
((eq (car sym) 'C-ref) nil)
|
|
((and (member 'def flags) (member 'ref flags))
|
|
'gas-hi-ok)
|
|
((> (length (cadr sym-list)) 1) 'gas-hi-undef)))
|
|
(dmsg 'sym "target-list, sym: %s, list: %s" sym target-list)
|
|
(when target-list
|
|
(add-to-list target-list (cdr sym) t))))
|
|
(setq gas-hi-valid t)
|
|
(font-lock-fontify-buffer)))
|
|
(setq case-fold-search case-fold-orig)))))
|
|
|
|
(defun gas-symbol-highlight-maybe ()
|
|
"Check for a symbol at point to be highlighted.
|
|
For use with the idle timer."
|
|
(unless (or (not gas-enable-symbol-highlight ) (input-pending-p))
|
|
(undo-boundary) ; probably redundant
|
|
(gas-symbol-highlight)
|
|
(undo-boundary))) ; -"-
|
|
|
|
|
|
(defun gas-C-comment-p ()
|
|
"True if we're editing a C syntax comment (the one enclosed in /* */)."
|
|
(save-excursion
|
|
(let ((current (point)))
|
|
(cond ((not (re-search-backward "/\\*" 0 t)) nil)
|
|
((not (re-search-forward "\\*/" current t)))))))
|
|
|
|
;; format of a C passthrough:
|
|
;; --- asm
|
|
;; /*C
|
|
;; --- C passthrough code
|
|
;; /#
|
|
;; --- C passthrough comment
|
|
;; #/
|
|
;; ---- C passthrough code
|
|
;; [... passthrough code, comment as above ...]
|
|
;; */
|
|
;; --- asm
|
|
(defun gas-C-passthrough-p ()
|
|
"True if we're editing a C passthrough (a C style comment enclosed in /*C ...
|
|
*/)"
|
|
(when gas-use-C-passthrough
|
|
(save-excursion
|
|
(let ((current (point)))
|
|
(and (re-search-backward "/\\*C" 0 t)
|
|
(not (re-search-forward "\\*/" current t)))))))
|
|
|
|
(defun gas-C-passthrough-comment-p ()
|
|
"True if we're editing a C passthrough comment (/# ...
|
|
#/ within a /*C ... */ comment)"
|
|
(when gas-use-C-passthrough
|
|
(save-excursion
|
|
(let ((current (point)))
|
|
(when (gas-C-passthrough-p)
|
|
(save-restriction
|
|
(prog2
|
|
(gas-narrow-to-C-comment)
|
|
(and (re-search-backward "/\\#" 0 t)
|
|
(not (re-search-forward "[#*]/" current t))))))))))
|
|
|
|
|
|
(defun gas-C-passthrough-code-p ()
|
|
"True if we're editing C passthrough code.
|
|
i.e. we're within a /*C ...*/ comment but not within /# ... #/."
|
|
(when gas-use-C-passthrough
|
|
(and (gas-C-passthrough-p)
|
|
(not (gas-C-passthrough-comment-p)))))
|
|
|
|
(defun gas-C-comment-really-p ()
|
|
"True if we're in a standard C commont or in a nested passthrough C comment."
|
|
(and (gas-C-comment-p)
|
|
(not (gas-C-passthrough-code-p))))
|
|
|
|
(defun gas-comment-p ()
|
|
"True if we're editing some kind of comment."
|
|
(or (looking-back (concat gas-comment-string ".*"))
|
|
(gas-C-comment-p)))
|
|
|
|
(defun gas-narrow-to-C-comment ()
|
|
"Narrow region to the C comment point resides in."
|
|
(unless (gas-C-comment-p)
|
|
(error "Not within C comment"))
|
|
(save-excursion
|
|
(let* ((beg (re-search-backward ".*/[*]"))
|
|
(end (re-search-forward "[*]/" nil t)))
|
|
(when end
|
|
(narrow-to-region beg end)))))
|
|
|
|
|
|
(defun gas-token-pattern (cur-check)
|
|
"Return a list how to parse for token `CUR-CHECK'.
|
|
The returned list holds three elements, see the documentation
|
|
of `gas-patterns' for an explanation."
|
|
(let ((pattern (cdr (assq cur-check gas-patterns))))
|
|
(when (not pattern)
|
|
(t (error "Gas internal: illegal pattern %s requested"
|
|
cur-check)))
|
|
pattern))
|
|
|
|
(defun gas-next-token (check-list)
|
|
"Parse for one of the tokens in list `CHECK-LIST'.
|
|
`CHECK-LIST' is a list of cons cells (TOKEN-SYMBOL . RESULTING-TYPE).
|
|
|
|
Look up each TOKEN-SYMBOL in `gas-patterns' in order, then match
|
|
the text at point against the two regexps found there. On match,
|
|
eat the match (advance point past it) and return the resulting
|
|
field with its car set to RESULTING-TYPE (which is the same as
|
|
TOKEN-SYMBOL in nearly all cases except for the final parser
|
|
state, 'fini (and may be nil if same)). If none matches,
|
|
return nil.
|
|
|
|
Valid TOKEN-SYMBOLS and RESULTING-TYPEs are the ones listed in
|
|
'gas-patterns'."
|
|
|
|
(save-match-data
|
|
(catch 'got-token
|
|
(dolist (cur-check check-list)
|
|
(save-restriction
|
|
(let* ((pattern-list (gas-token-pattern (car cur-check)))
|
|
(regexp (pop pattern-list))
|
|
(subexps (pop pattern-list))
|
|
(limit-re (pop pattern-list))
|
|
limit
|
|
(subtype nil))
|
|
(setq limit (line-end-position))
|
|
(when (and limit-re
|
|
(looking-at
|
|
(format "\\(\\(\"[^\n\"]*\"\\)\\|[^\n\"]\\)*?\\(%s\\)"
|
|
limit-re)))
|
|
(setq limit (match-beginning 3)))
|
|
(when (>= limit (point))
|
|
(narrow-to-region (point) limit)
|
|
(when (looking-at regexp) ; match pattern
|
|
(cond
|
|
((eq (cdr cur-check) 'asm-comment)
|
|
;; set subtype to the number of consecutive comment-chars
|
|
(setq subtype (- (match-end 2) (match-beginning 2))))
|
|
((eq (cdr cur-check) 'argument)
|
|
;; set subtype to be a list holding the positions of the
|
|
;; individual subexpressions (relative to start of text)
|
|
(let ((arg-beg (point))
|
|
(arg-end (match-end 0)))
|
|
(setq subtype '(0))
|
|
(save-excursion
|
|
(save-match-data
|
|
(while (re-search-forward "," arg-end t)
|
|
(add-to-list 'subtype (- (point) arg-beg 1) t)))))))
|
|
(goto-char (min limit (match-end 0)))
|
|
(throw 'got-token
|
|
(list (cdr cur-check)
|
|
subtype
|
|
(- (min limit (match-beginning (pop subexps))) curbol)
|
|
(- (min limit (match-beginning (pop subexps))) curbol)
|
|
(- (min limit (match-end (pop subexps))) curbol)
|
|
(let ((subexp (pop subexps)))
|
|
(and subexp
|
|
(not (equal (match-string subexp) ""))
|
|
(match-string subexp)))
|
|
nil))))))))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(defun gas-parse-line-really ()
|
|
"Parse the line point is on, element by element.
|
|
The returned list holds a list of syntactic elements (fields)
|
|
found, in the order of appearance. Each syntactic element is
|
|
represented by a list holding 7 elements:
|
|
|
|
'(TYPE SUBTYPE BEG-COL TEXT-COL END-COL TEXT MODIFIED)
|
|
0 - 'type: ('label, 'opcode ...)
|
|
1 - 'subtype: (additional information required by some types)
|
|
2 - 'beg-col: first slot (column #) (usually occupied by white
|
|
space before text)
|
|
3 - 'text-col: first text slot (column #)
|
|
4 - 'end-col: first free slot after field (column #)
|
|
5 - 'text: text contents (nil if empty)
|
|
6 - 'modified: nil (will later reflect if rearrangement (indentation)
|
|
is required / was done).
|
|
|
|
Terminology: A single list of these 7 elements is called a
|
|
`field' throughout gas-mode.el. `fields' as symbol name (or part
|
|
of a symbol name) means it stands for a variable bound to a list
|
|
of `field' lists which, in order of appearance, describe a
|
|
complete source line.
|
|
|
|
Note that text-col may lie outside (on the right side of) the
|
|
range beg-col ... end-col if there's no actual text (it is the
|
|
column where text would have to go to).
|
|
|
|
Don't assume a fixed element position except for 'type, which is
|
|
always the car of the list. Instead, use `gas-nth' to extract
|
|
elements by their symbolic names ('type, 'subtype, ...)."
|
|
|
|
;; We use syntax tables neither here nor much throughout the whole
|
|
;; gas-mode, since assembler code, being line oriented and based on
|
|
;; number and position of elements on the line, fits regexps better
|
|
;; than syntax tables (IMHO). The only drawback is that this
|
|
;; approach makes dealing with inline C-style comments embedded
|
|
;; between asm fields (who the hell does that?) somewhat clumsy,
|
|
;; but not by much.
|
|
(let (field
|
|
(f-type 'start)
|
|
pushed-f-type
|
|
(C-inline-comment-level 0)
|
|
fields)
|
|
|
|
(save-excursion
|
|
(setq cureol (line-end-position))
|
|
(beginning-of-line)
|
|
(setq curbol (point))
|
|
|
|
(setq f-type
|
|
(if (gas-C-comment-p)
|
|
(progn
|
|
(setq pushed-f-type 'starting-asm-line)
|
|
'starting-within-C-comment)
|
|
'starting-asm-line))
|
|
(while f-type
|
|
;; always check for C-comment first (except we're already
|
|
;; inside)
|
|
(setq field
|
|
(and (not (gas-C-comment-p))
|
|
(gas-next-token
|
|
'((C-inline-comment . C-inline-comment)
|
|
(C-comment-start . C-comment-start)))))
|
|
(unless field
|
|
;; dispatch based on previous field
|
|
(let ((next-check (cdr (assq f-type gas-parse-sequences))))
|
|
(when (not next-check)
|
|
(error "Gas-mode internal: gas-parse-line: %s" f-type))
|
|
(when (not (eq next-check 'fini))
|
|
;; parse:
|
|
(setq field (gas-next-token next-check)))
|
|
(when (eq (car field) 'garbage)
|
|
(error "Gas-mode internal: garbage: %s" field))
|
|
(dmsg 'parser "parsed a field: %s->%s" f-type field)))
|
|
(when (eq (car field) 'C-inline-comment)
|
|
(setcar field (list 'C-inline-comment C-inline-comment-level))
|
|
(setq C-inline-comment-level (1+ C-inline-comment-level)))
|
|
(when (and (eq 'asm-comment (car field))
|
|
(assq 'opcode fields))
|
|
(gas-set-nth 'subtype field 1))
|
|
(when field
|
|
(add-to-list 'fields field t))
|
|
(when (eq (car field) 'C-comment-start) ; push state
|
|
(setq pushed-f-type f-type))
|
|
(unless (and field (listp (car field))) ; not C-inline-comment
|
|
(setq f-type (car field)))
|
|
(when (eq f-type 'C-comment-end) ; pop state
|
|
(setq f-type pushed-f-type))
|
|
(when (listp f-type)
|
|
(setq f-type (car f-type)))))
|
|
|
|
|
|
(dmsg '(parser cursor) "parsed all fields: %s" fields)
|
|
fields))
|
|
|
|
|
|
(defun gas-get-field (field-type field-list &optional offset)
|
|
"Return field of specified type.
|
|
|
|
Return field (a list) with first element (syntactic type) eq
|
|
to FIELD-TYPE from FIELD-LIST, nil if no such field.
|
|
|
|
Optional OFFSET >= 0: return next field, OFFSET < 0: return
|
|
previous field. If OFFSET is given. both fields (FIELD-TYPE and
|
|
the one before/after) must exist, else nil is returned."
|
|
(if offset
|
|
(let (f-types
|
|
f-type)
|
|
(dolist (field field-list)
|
|
(add-to-list 'f-types (car field) t))
|
|
(if (> offset 0)
|
|
(while (not (eq (pop ftypes) field-type)))
|
|
(while (and (setq f-type (nth 1 f-types))
|
|
f-type
|
|
(not (eq f-type field-type)))
|
|
(pop f-types)))
|
|
(gas-get-field (car f-types) field-list))
|
|
(assq field-type field-list)))
|
|
|
|
(defun gas-nth (component field-from &optional field-list)
|
|
"Get one of the components of a field.
|
|
|
|
COMPONENT is the component to be returned. FIELD-FROM is either
|
|
a list containing a field, or a symbol designating a field (see
|
|
`gas-parse-line-really' for both).
|
|
|
|
In the latter case, optional FIELD-LIST must be supplied as a
|
|
list of fields where FIELD-FROM is extracted from."
|
|
(let ((field-from (if field-list
|
|
(gas-get-field field-from field-list)
|
|
field-from)))
|
|
(nth (gas-elmt-n component) field-from)))
|
|
|
|
(defun gas-elmt-n (elmt)
|
|
"Return the numeric index of field component ELMT."
|
|
(when (symbolp elmt)
|
|
(- (length gas-elmt-types) (length (memq elmt gas-elmt-types)))))
|
|
|
|
(defun gas-set-nth (elmt field val-or-fields &optional val)
|
|
"Set a single field component.
|
|
|
|
ELMT is the content to set the field to.
|
|
If FIELD is a list containing a single field, set its ELMT (a
|
|
symbol) to VAL-OR-FIELDS. If FIELD is a symbol designating a
|
|
field and VAL-OR-FIELDS is a list of such fields (as returned by
|
|
`gas-parsed'), set ELMT in the FIELD field of VAL-OR-FIELDS to
|
|
VAL."
|
|
(let ((curfield field))
|
|
(if val
|
|
(setq curfield (assq field val-or-fields))
|
|
(setq val val-or-fields))
|
|
(setcar (nthcdr (gas-elmt-n elmt) curfield) val)))
|
|
|
|
(defun gas-rearrange (fields &optional elmt f-type val)
|
|
"Rearrange FIELDS so they fit together without gaps or overlapping.
|
|
|
|
FIELDS is the field list describing the current line.
|
|
|
|
Optional ELMT designates a field component in the field specified
|
|
by (eq to) F-TYPE, which has to be changed before rearrangement
|
|
to VAL. VAL may be a variable, a symbol which evaluates to the
|
|
taget value, or bound to a function returning the target type of
|
|
the component.
|
|
|
|
If the line did change, return the field type (the car) of the
|
|
first field that has changed, else nil."
|
|
(let ((field-slot 0) ; next free slot for a field
|
|
(text-slot 0) ; next column where text should go to
|
|
(end-slot 0)
|
|
did-change)
|
|
(dolist (field fields)
|
|
(let ((old-col (gas-nth 'text-col field))
|
|
(text (gas-nth 'text field)))
|
|
|
|
(gas-set-nth 'beg-col field field-slot)
|
|
;; insert new value if needed
|
|
(when (and val (eq f-type (gas-nth 'type field)))
|
|
(gas-set-nth elmt field val))
|
|
;; determine text column
|
|
(let ((tcol (gas-nth 'text-col field)))
|
|
;; if the current text column is nil and we are reordering:
|
|
;; put in its default value.
|
|
(when (or (eq elmt 'all)
|
|
(and (not val) (eq f-type (car field))))
|
|
(setq tcol (cdr (assq (car field) gas-indents))))
|
|
;; if column is a symbol: replace it by its value
|
|
(when (and tcol (symbolp tcol))
|
|
(setq tcol (if (fboundp tcol)
|
|
(funcall tcol)
|
|
(eval tcol))))
|
|
;; if we still have got no target value: use the next free
|
|
;; text slot
|
|
(unless tcol
|
|
(setq tcol text-slot))
|
|
;; set text col to max (current, next-free-slot)
|
|
(setq tcol (max tcol text-slot))
|
|
(gas-set-nth 'text-col field tcol)
|
|
;; update `modified' flags:
|
|
(when (and text
|
|
(not (eq tcol old-col)))
|
|
(unless did-change
|
|
(setq did-change (car field)))
|
|
(gas-set-nth 'modified field t))
|
|
(when text
|
|
(setq field-slot (+ (gas-nth 'text-col field) (length text))))
|
|
(setq end-slot (max end-slot (or (gas-nth 'end-col field) 0)))
|
|
(gas-set-nth 'end-col field field-slot)
|
|
;; determine next free slot
|
|
(setq text-slot (max tcol text-slot))
|
|
(when (/= 0 field-slot)
|
|
(setq text-slot (max text-slot (1+ field-slot)))))))
|
|
(gas-set-nth 'end-col 'eol-ws fields end-slot)
|
|
did-change))
|
|
|
|
|
|
(defun gas-reset-modified (fields)
|
|
"Clears the `modified' flag on all fields in FIELDS."
|
|
(dolist (field fields)
|
|
(gas-set-nth 'modified field nil)))
|
|
|
|
(defun gas-get-pointpos (ffields)
|
|
"Determine the field in FFIELDS point is on.
|
|
Returns a list (F-TYPE SUBTYPE OFFSET-TO-FIELD-TEXT-BEGIN ABS-COLUMN EOL).
|
|
EOL may bei either the symbol 'eol or nil"
|
|
(let* ((curcol (current-column))
|
|
(fields (copy-tree ffields))
|
|
(ppos (list (caar fields) curcol)))
|
|
(catch 'pos-found
|
|
(dolist (field fields)
|
|
;; first, look for a field match
|
|
(when (and (gas-nth 'text field)
|
|
(>= curcol (gas-nth 'beg-col field)) ; field start
|
|
(<= curcol (gas-nth 'end-col field))) ; field end
|
|
(setq ppos (list (car field) (- curcol (gas-nth 'text-col field))))
|
|
(throw 'pos-found t)))
|
|
;; no field match: look at position relative to text slot
|
|
(gas-rearrange fields 'all)
|
|
(dolist (field fields)
|
|
(when (or (<= curcol (gas-nth 'text-col field))
|
|
(eq (car field) 'eol-ws))
|
|
(setq ppos (list (car field) (- curcol (gas-nth 'text-col field))))
|
|
(throw 'pos-found t))))
|
|
(setq ppos (append ppos (list curcol)))
|
|
(if (>= curcol (gas-nth 'end-col 'eol-ws fields))
|
|
(append ppos '(eol))
|
|
ppos)))
|
|
|
|
(defun gas-get-C-relative-indent ()
|
|
"Return indent of the closest previous non-blank line in current C comment."
|
|
(if (not (gas-C-comment-p))
|
|
gas-C-indent
|
|
(save-restriction
|
|
(gas-narrow-to-C-comment)
|
|
(let ((col
|
|
(save-excursion
|
|
(catch 'found
|
|
(if (looking-at "[ \t]*#/")
|
|
;; end passthrough: same indent as start
|
|
(while (= 0 (forward-line -1))
|
|
(when (looking-at "[ \t]*/#")
|
|
(throw 'found (current-indentation))))
|
|
(while (= 0 (forward-line -1))
|
|
(when (looking-at "[ \t]*/[*#]")
|
|
(throw 'found (+ gas-C-indent (current-indentation))))
|
|
(when (looking-at "[ \t]*[^ \t\n]")
|
|
(throw 'found (current-indentation)))))
|
|
gas-C-indent))))
|
|
(when (gas-C-passthrough-code-p)
|
|
(if (looking-at "[ \t\n]*}[ \t\n]*;")
|
|
(setq col (max 0 (- col gas-C-indent)))))
|
|
col))))
|
|
|
|
(defun gas-add-missing-fields (parsed-fields)
|
|
"Complete parser result by adding in empty but allowed fields.
|
|
|
|
Determine line syntax from the parse result in PARSED-FIELDS,
|
|
then add in empty fields for syntactic components not used in the
|
|
source line but allowed by the syntax, so later code can safely
|
|
assume they are present."
|
|
;; Let's qualify what we've got
|
|
(let (existing-types mandatory line-syntax new-fields f-type cur-type)
|
|
;; determine mandatory fields:
|
|
(setq mandatory
|
|
(cond ((not parsed-fields) ; empty line
|
|
'(label opcode argument asm-comment eol-ws))
|
|
((assq 'C-comment parsed-fields)
|
|
'(C-comment-start C-comment C-comment-end eol-ws))
|
|
((assq 'cpp-macro-def parsed-fields)
|
|
'(cpp-macro-def cpp-argument eol-ws))
|
|
(t
|
|
(let ((subtype (gas-nth 'subtype 'asm-comment parsed-fields)))
|
|
(cond
|
|
((eq subtype 3) '(asm-comment eol-ws))
|
|
((eq subtype 2) '(label asm-comment eol-ws))
|
|
(t '(label opcode argument asm-comment eol-ws)))))))
|
|
(dmsg '(parser cursor) "mandatory: %s" mandatory)
|
|
;; We're going through all fields in `fields', inserting mandatory
|
|
;; elements (from `mandatory') if not present
|
|
(dolist (field parsed-fields)
|
|
(setq f-type (car field))
|
|
(if (member f-type mandatory)
|
|
;; we have a mandatory field. Insert missing fields (if
|
|
;; any) before it, then insert the field at its place.
|
|
(progn
|
|
;; insert an empty field for each mandatory but
|
|
;; not already exisiting element before the one
|
|
;; we're dealing with.
|
|
(while (progn
|
|
(setq cur-type (pop mandatory))
|
|
(not (eq cur-type f-type)))
|
|
(dmsg 'parser "adding missing mandatory: %s" cur-type)
|
|
(add-to-list 'new-fields
|
|
(list cur-type nil nil nil nil nil nil) t))
|
|
;; now insert field
|
|
(dmsg 'parser "adding existing mandatory: %s" field)
|
|
(add-to-list 'new-fields field t))
|
|
;; we have a non-mandatory field. Pass it through,
|
|
;; keeping its place if possible.
|
|
(add-to-list 'new-fields field t)))
|
|
;; done with parsed fields.
|
|
;; add mandatory elements left out at right
|
|
(while (setq cur-type (pop mandatory))
|
|
(dmsg 'rearranged "adding left-over: %s" cur-type)
|
|
(add-to-list 'new-fields
|
|
(list cur-type nil nil nil nil nil nil) t))
|
|
(dmsg 'parser "new-fields=%s" new-fields)
|
|
new-fields))
|
|
|
|
(defun gas-parsed (&optional lineno)
|
|
"Return fields on line LINENO or, if nil, on current line.
|
|
Return cached data, if available. Else, call
|
|
`gas-parse-line-really', store result into the gas-line-cache and
|
|
return it."
|
|
(dmsg 'parser "gas-changed: %s, lenght gas-line-cache: %s"
|
|
gas-changed (length gas-line-cache))
|
|
(unless (and (eq gas-comment-char ?\;)
|
|
(eq gas-local-comment-char gas-comment-char))
|
|
(setq gas-local-comment-char gas-comment-char)
|
|
(gas-set-patterns gas-comment-char))
|
|
(when gas-changed
|
|
(setq gas-line-cache nil)
|
|
(setq gas-changed nil))
|
|
(when (> (length gas-line-cache) gas-max-lines-in-cache)
|
|
(dmsg 'parser "gas-line-cache truncated.")
|
|
(nbutlast gas-line-cache (/ (* gas-max-lines-in-cache 3) 4)))
|
|
(let* ((curline (or lineno (line-number-at-pos)))
|
|
(cached (assq curline gas-line-cache)))
|
|
(if cached
|
|
(cadr cached)
|
|
(let (fields)
|
|
(save-excursion
|
|
(goto-line curline)
|
|
(setq fields (gas-parse-line-really))
|
|
(setq fields (gas-add-missing-fields fields))
|
|
(gas-rearrange fields)
|
|
(dmsg 'parser "->gas-line-cache: %s" fields)
|
|
(add-to-list 'gas-line-cache (list curline fields)))
|
|
fields))))
|
|
|
|
(defun gas-put-parsed (fields lineno)
|
|
"Put FIELDS into gas-line-cache at LINENO.
|
|
Overwrites the previous entry for that line (if any)."
|
|
(gas-reset-modified fields)
|
|
(let ((current (assq lineno gas-line-cache)))
|
|
(if current
|
|
(setcdr current (list fields))
|
|
(add-to-list 'gas-line-cache (list curline fields)))))
|
|
|
|
(defun gas-put-out (&optional fields)
|
|
"Replace the current scrren line by the one described by FIELDS."
|
|
(when (not fields)
|
|
(setq fields (gas-parsed)))
|
|
(dmsg 'indent "put-out, fields: %s" fields)
|
|
(combine-after-change-calls
|
|
(save-excursion
|
|
(beginning-of-line)
|
|
(delete-region (point) (line-end-position))
|
|
(dolist (field fields)
|
|
(when (and (gas-nth 'text field)
|
|
(or gas-preserve-trailing-whitespace
|
|
(not (eq (car field) 'eol-ws))))
|
|
(indent-to (gas-nth 'text-col field))
|
|
(insert (gas-nth 'text field)))))))
|
|
|
|
(defun gas-get-asm-comment-column ()
|
|
"Get comment column according to asm comment subtype on current line.
|
|
The caller must provide the variable `fields' (bound to current
|
|
line's parsed content)."
|
|
(let ((subtype (gas-nth 'subtype 'asm-comment fields)))
|
|
(cond ((not subtype) gas-comment-column)
|
|
((= subtype 1) gas-comment-column)
|
|
((= subtype 2) gas-opcode-column)
|
|
(t 0))))
|
|
|
|
(defun gas-set-point-to-field (f-type fields)
|
|
"Postition point to the start of the field of type F-TYPE.
|
|
FIELDS is a field list reflecting the current line."
|
|
(let ((target-col (gas-nth 'text-col f-type fields)))
|
|
(move-to-column target-col)
|
|
(indent-to target-col)
|
|
(when (eq f-type 'asm-comment)
|
|
(skip-chars-forward (format " %c" gas-comment-char))
|
|
(when (not (looking-back " "))
|
|
(insert-char ?\s 1)))))
|
|
|
|
(defun gas-set-point (pointpos fields &optional direction always)
|
|
"Set point to a field of the current line.
|
|
|
|
POINTPOS is the current field as returned by a call to
|
|
`gas-get-pointpos', FIELDS the parsed content of the current
|
|
line.
|
|
|
|
If DIRECTION is nil or 'stay, set it to the beginning of
|
|
the current field. If DIRECTION is 'end-of-field, set it to the
|
|
end of the current field. If DIRECTION equals 'backward or
|
|
'forward, and ALWAYS is nil, set it to the beginning of the
|
|
next/previous field only if the field is unchanged, else to the
|
|
beginning of the current. Finally, if DIRECTION equals 'backward
|
|
or'forward and ALWAYS is non-nil, move point unconditionally to
|
|
the beginning of the next/previous field."
|
|
(let ((existing '(line-begin))
|
|
f-type)
|
|
(dmsg 'indent "(gas-set-point %s %s %s)" pointpos direction always)
|
|
(dolist (field fields)
|
|
(setq existing (append existing (list (car field)))))
|
|
(add-to-list 'existing 'line-end t)
|
|
(when (eq direction 'backward)
|
|
(setq existing (nreverse existing)))
|
|
(setq f-type (car pointpos))
|
|
(when (and (eq direction 'forward)
|
|
(eq (nth 3 pointpos) 'eol))
|
|
;; special forward skipping rules for certain fields:
|
|
(cond ((and (eq f-type 'C-comment-start) (= (nth 2 pointpos) 0))
|
|
;; move on:
|
|
nil)
|
|
((memq f-type '(label opcode argument))
|
|
(cond ((>= (nth 2 pointpos) gas-comment-column)
|
|
(setq f-type 'eol-ws)) ; next: line-end
|
|
((and (not (gas-nth 'text 'opcode fields))
|
|
(>= (nth 2 pointpos) gas-opcode-column))
|
|
(setq f-type 'argument)))) ; next: comment
|
|
(t (setq f-type 'eol-ws)))) ; next: line-end
|
|
;; rules for skipping backward:
|
|
(when (eq direction 'backward)
|
|
(when (= (nth 2 pointpos) 0)
|
|
(setq f-type (car (last existing 2)))) ;next: line-begin
|
|
(and (> (nth 1 pointpos) 0)
|
|
(or (not (eq f-type 'asm-comment))
|
|
(not (looking-back ";[ \t]")))
|
|
(setq direction 'stay)))
|
|
;; move to next field if told to do so:
|
|
(when (and (not (memq direction '(pos stay end-of-field))) ; no - forbidden
|
|
(or always ; yes - do always
|
|
(and direction ; maybe - do only if field unchanged
|
|
(not (gas-nth 'modified f-type fields)))))
|
|
(setq f-type (cadr (member f-type existing))))
|
|
(dmsg 'indent "gas-set-point next:%s, fields: %s" f-type fields)
|
|
;; set point
|
|
(if (eq f-type 'line-begin)
|
|
(progn
|
|
(if (bobp)
|
|
(beep))
|
|
(beginning-of-line))
|
|
(when (eq f-type 'line-end)
|
|
(when (eobp)
|
|
(beep)
|
|
(error "End of buffer"))
|
|
(setq f-type 'eol-ws)
|
|
(setq direction 'end-of-field))
|
|
(gas-put-out)
|
|
;; and set point there:
|
|
(let ((where (gas-nth 'text-col f-type fields)))
|
|
(cond ((eq direction 'pos)
|
|
(setq where (+ (gas-nth 'text-col f-type fields)
|
|
(cadr pointpos))))
|
|
((eq direction 'end-of-field)
|
|
(setq where (max where (gas-nth 'end-col f-type fields)))))
|
|
(move-to-column where t)
|
|
(indent-to where))
|
|
(when (eq f-type 'asm-comment)
|
|
(skip-chars-forward (format " %c" gas-comment-char))
|
|
(when (not (looking-back " "))
|
|
(insert-char ?\s 1))))))
|
|
|
|
(defun gas-indent (&optional direction always ffields)
|
|
"Indent current line.
|
|
|
|
For lines carrying asm syntax, `gas-indent-current-field-only'
|
|
determines if only the current field or the entire line is
|
|
affected.
|
|
|
|
See `gas-set-point' for a description of DIRECTION and ALWAYS.
|
|
FFIELDS, if present, is a list describing the fields on the
|
|
current line as returned by `gas-parsed'."
|
|
(let* ((fields (or ffields (gas-parsed)))
|
|
rearranged-type
|
|
pointpos)
|
|
(setq pointpos (gas-get-pointpos fields))
|
|
(dmsg 'indent "gas-indent, fields: %s" fields)
|
|
(dmsg 'indent "gas-indent, pointpos: %s" pointpos)
|
|
(when (and (not direction)
|
|
(not (gas-C-comment-p))
|
|
(eq this-command 'newline-and-indent))
|
|
;; go to first non-empty field. If none, to opcode.
|
|
(setq pointpos
|
|
(list
|
|
(or (catch 'field-found
|
|
(dolist (field fields)
|
|
(when (gas-nth 'text field)
|
|
(throw 'field-found (car field)))))
|
|
'opcode)
|
|
0 gas-opcode-column))
|
|
(setq direction 'stay)
|
|
(setq always t))
|
|
(setq rearranged-type
|
|
(cond
|
|
((and (eq (caar fields) 'C-comment-start)
|
|
(or (save-excursion (beginning-of-line)(looking-at "[ \t]*$"))
|
|
(and (eq (car pointpos) 'C-comment) (= (nth 1 pointpos) 0))))
|
|
;; at start of C comment text (poosibly empty): in/outdent
|
|
(let ((n_indents (/ (nth 2 pointpos) gas-C-indent)))
|
|
(if (eq direction 'backward)
|
|
(setq n_indents (max 0 (1- n_indents)))
|
|
(setq n_indents (1+ n_indents)))
|
|
(gas-rearrange fields 'text-col 'C-comment
|
|
(* gas-C-indent n_indents))
|
|
(setq direction 'stay)
|
|
'C-comment))
|
|
(gas-indent-current-field-only
|
|
(gas-rearrange fields 'text-col (car pointpos)
|
|
(cdr (assq (car pointpos) gas-indents))))
|
|
(t (gas-rearrange fields 'all))))
|
|
(dmsg 'indent "re-indented, fields: %s" fields)
|
|
(gas-put-parsed fields (line-number-at-pos))
|
|
(gas-put-out)
|
|
(cond (always (gas-set-point pointpos fields direction always))
|
|
(rearranged-type (gas-set-point-to-field rearranged-type fields))
|
|
((and (eq (car pointpos) 'C-comment) (not direction)
|
|
(> (cadr pointpos) 0))
|
|
(gas-set-point pointpos fields 'pos always))
|
|
((and (not direction) (>= (cadr pointpos) 0))
|
|
(gas-set-point pointpos fields 'forward always))
|
|
(t (gas-set-point pointpos fields direction always)))
|
|
fields))
|
|
|
|
(defun gas-indent-region (&optional from to)
|
|
"Indent all fields in region.
|
|
|
|
If optional FROM and TO are given, they are used instead of point
|
|
and mark for the region's end points."
|
|
(interactive)
|
|
(unless from (setq from (min (point) (mark))))
|
|
(unless to (setq to (max (point) (mark))))
|
|
(combine-after-change-calls
|
|
(save-excursion
|
|
(goto-char from)
|
|
(while (and (not (eobp)) (< (point) to))
|
|
(dmsg 'indent "indent-region, line:%s" (line-number-at-pos))
|
|
(if (gas-C-comment-p)
|
|
(beginning-of-line 2) ; skip C comments
|
|
(let ((fields (gas-parsed)))
|
|
(gas-rearrange fields 'all)
|
|
(gas-put-out fields)
|
|
(beginning-of-line 2)))))))
|
|
|
|
(defun gas-indent-backward ()
|
|
"Indent, then move to previous field.
|
|
While in C-comment, remove one level of indentation."
|
|
(interactive)
|
|
(gas-indent 'backward 'always))
|
|
|
|
(defun gas-forward-sexp (n)
|
|
"On a highlighted symbol, move to next (previous if N < 9).
|
|
Else do `forward-sexp' as usual."
|
|
(gas-sym-invalidate)
|
|
(interactive)
|
|
(gas-symbol-highlight)
|
|
(if gas-hi-sym-list
|
|
(unless
|
|
(catch 'found-one
|
|
(if (< 0 n)
|
|
(dolist (match (cadr gas-hi-sym-list))
|
|
(when (> (cadr match) (point))
|
|
(goto-char (cadr match))
|
|
(throw 'found-one t)))
|
|
(dolist (match (reverse (cadr gas-hi-sym-list)))
|
|
(when (< (nth 2 match) (point))
|
|
(goto-char (cadr match))
|
|
(throw 'found-one t)))))
|
|
(beep))
|
|
(let (forward-sexp-function)
|
|
(forward-sexp n))))
|
|
|
|
|
|
(defun gas-beginning-of-defun ()
|
|
"Skip to the beginning of the current block.
|
|
The block delimiter is described by `gas-defun-regexp'."
|
|
(interactive)
|
|
(let* ((beg (save-excursion
|
|
(re-search-backward gas-defun-regexp nil t))))
|
|
(when beg
|
|
(goto-char (match-beginning gas-defun-regexp-subexp)))))
|
|
|
|
(defun gas-end-of-defun ()
|
|
"Skip to the beginning of the next block.
|
|
The block delimiter is described by `gas-defun-regexp'."
|
|
(interactive)
|
|
(let ((beg (save-excursion
|
|
(re-search-forward gas-defun-regexp nil t))))
|
|
(when beg
|
|
(goto-char (match-beginning gas-defun-regexp-subexp)))))
|
|
|
|
|
|
(defun gas-hash ()
|
|
"Insert a hash mark. If it start a macro, delete the indentaion."
|
|
(interactive)
|
|
(when (and (not (gas-comment-p))
|
|
(looking-back "^[ \t]*"))
|
|
(beginning-of-line)
|
|
(delete-horizontal-space))
|
|
(call-interactively 'self-insert-command))
|
|
|
|
|
|
(defun gas-colon ()
|
|
"Insert a colon; if it follows a label, delete the label's indentation."
|
|
(interactive)
|
|
(call-interactively 'self-insert-command)
|
|
;; check if colon belongs to a label field
|
|
(let* ((fields (gas-parsed))
|
|
(pointpos (gas-get-pointpos fields)))
|
|
(dmsg 'indent "gas-colon, fields: %s" fields)
|
|
(dmsg 'indent "gas-colon, pointpos: %s" pointpos)
|
|
(when (eq (car pointpos) 'label)
|
|
(beginning-of-line)
|
|
(gas-indent 'forward t))))
|
|
|
|
|
|
;; Obsolete since Emacs-22.1.
|
|
(defalias 'gas-newline 'newline-and-indent)
|
|
|
|
(defun gas-comment-region-dwim (&optional from to)
|
|
"De-comment region if at start of comment, make comment out of region if not.
|
|
Optional argument FROM Optional FROM and TO mark the region."
|
|
(unless from
|
|
(setq from (min (point) (mark)))
|
|
(setq to (max (point) (mark))))
|
|
(when (save-excursion (goto-char to) (bolp))
|
|
(setq to (1- to)))
|
|
(save-excursion
|
|
(let ((mmax (progn (goto-char to) (point-marker)))
|
|
(mmin (progn (goto-char from) (point-marker))))
|
|
|
|
(if (gas-C-passthrough-p)
|
|
(let* (beg end end-match
|
|
(pos (looking-at "[ \t\n]*/#"))
|
|
(de-comment (or pos (gas-C-passthrough-comment-p))))
|
|
(save-restriction
|
|
(gas-narrow-to-C-comment)
|
|
(if de-comment
|
|
(progn
|
|
(when (gas-C-passthrough-comment-p)
|
|
(insert " #/\n"))
|
|
(while (re-search-forward
|
|
"\\(\n[ \t]*\\)?/#\\(\n[ \t]*\\)?" mmax t)
|
|
(replace-match "")
|
|
(when (re-search-forward
|
|
"\\(\n[ \t]*\\)?[#]/\\(\n[ \t]*\\)?" mmax t)
|
|
(replace-match "")))
|
|
(let* ((end (save-excursion
|
|
(re-search-forward "[#*]/" nil t)))
|
|
(end-match (match-string 0))
|
|
(beg (save-excursion
|
|
(re-search-forward "/#" nil t))))
|
|
(when (and (equal end-match "#/")
|
|
(or (not beg)
|
|
(> beg end)))
|
|
;; we have a comment close outside the selcted
|
|
;; area which misses the matching comment open
|
|
(goto-char mmax)
|
|
(insert "\n/#\n"))))
|
|
;; do C en-comment
|
|
(insert " /#\n ") ;
|
|
(while (and (save-excursion
|
|
(setq end (re-search-forward "[*#]/" nil t)))
|
|
(setq beg (re-search-forward "/#" mmax t)))
|
|
(replace-match "#/ /# ")
|
|
(re-search-forward "[#*]/" mmax t)
|
|
(if (equal end-match "*/")
|
|
(replace-match " #/\n*/")
|
|
(replace-match "#/ /#")))
|
|
(let* ((end (save-excursion
|
|
(re-search-forward "[#*]/" nil t)))
|
|
(end-match (match-string 0))
|
|
(beg (save-excursion
|
|
(re-search-forward "/#" nil t))))
|
|
(when (and (equal end-match "*/")
|
|
(or (not beg)
|
|
(> beg end)))
|
|
;; we have no comment close outside the selcted
|
|
;; area but comment is open
|
|
(goto-char mmax)
|
|
(backward-char 2)
|
|
(insert "\n #/\n"))))))
|
|
;; skip to the start of the asm comment if we're on one
|
|
(when (looking-back (format "%c+[ \t]*" gas-comment-char))
|
|
(goto-char (match-beginning 0))
|
|
(skip-chars-backward gas-comment-string))
|
|
(save-restriction
|
|
(narrow-to-region (point) to)
|
|
(let ((triple-comment (concat (make-string 3 gas-comment-char) " "))
|
|
comment-pattern
|
|
(de-comment
|
|
;; de-comment if there's a comment after point or
|
|
;; at the start of the next non-empty line
|
|
(looking-at (format "[ \t]*%c\\|.*\n[ \t\n]*\%c"
|
|
gas-comment-char gas-comment-char))))
|
|
(when de-comment
|
|
(goto-char (match-end 0))
|
|
(skip-chars-backward gas-comment-string)
|
|
(looking-at (format "\n?\\([ \t]\\)*\\(%c+\\)\\([ \t]?\\)"
|
|
gas-comment-char))
|
|
(let ((pre-pattern (match-string 1))
|
|
(mid-pattern (regexp-quote (match-string 2)))
|
|
(post-pattern (match-string 3)))
|
|
(setq pre-pattern
|
|
(if (and pre-pattern
|
|
(string-match "[ \t]" pre-pattern))
|
|
"\\([ \t]*"
|
|
"\\("))
|
|
(setq post-pattern
|
|
(if (and post-pattern
|
|
(string-match "[ \t]" post-pattern))
|
|
"[ \t]?\\)"
|
|
(format "\\)\\([^%c]\\|$\\)" gas-comment-char)))
|
|
(setq comment-pattern
|
|
(concat pre-pattern mid-pattern post-pattern))))
|
|
(catch 'fini
|
|
(while t
|
|
(cond ((gas-C-comment-p) nil)
|
|
((looking-at "[ \t]/[*]") nil)
|
|
((not de-comment) (insert triple-comment))
|
|
(t (when (looking-at comment-pattern)
|
|
(replace-match "" nil t nil 1))))
|
|
;; forward-line returns t even after it just
|
|
;; moved to eol (end of narrowed region):
|
|
(unless (and (= 0 (forward-line)) (bolp))
|
|
(throw 'fini t)))))))
|
|
(setq mmax nil)
|
|
(setq mmin nil))))
|
|
|
|
(defun gas-backward-indent ()
|
|
"Skip to previous asm field. Within C style comments, decrease indentation."
|
|
(interactive)
|
|
(gas-indent 'backward 'always))
|
|
|
|
(defun gas-comment ()
|
|
"Start/expand a comment.
|
|
Suggested usage: while writing your code, trigger `gas-comment'
|
|
repeatedly until you are satisfied with the kind of comment."
|
|
(interactive)
|
|
(setq gas-doing-comment t)
|
|
(if mark-active
|
|
(gas-comment-region-dwim)
|
|
(let* ((fields (save-excursion (gas-parsed)))
|
|
(curcol (current-column))
|
|
(pointpos (gas-get-pointpos fields))
|
|
(subtype (gas-nth 'subtype 'asm-comment fields)))
|
|
(dmsg 'indent "gas-comment, pointpos: %s, fields. %s"
|
|
pointpos fields)
|
|
(cond
|
|
((gas-C-passthrough-comment-p) (insert-char gas-comment-char 1))
|
|
((gas-C-passthrough-code-p)
|
|
(when (looking-back "/[*]C[ \t]*")
|
|
(insert "\n"))
|
|
(if (looking-back "[ \t]")
|
|
(insert "/# #/ ")
|
|
(insert " /# #/ "))
|
|
(backward-char 5))
|
|
((gas-C-comment-p)
|
|
(if (and gas-use-C-passthrough
|
|
(looking-back "\\(/[*] ?\\)[ \t\n]*") )
|
|
(save-excursion (replace-match "/*C" nil nil nil 1))
|
|
(insert-char gas-comment-char 1)))
|
|
((and subtype (>= subtype 3))
|
|
(move-to-column (gas-nth 'text-col 'asm-comment fields))
|
|
(looking-at ";+ ?")
|
|
(replace-match "")
|
|
(insert "/*")
|
|
(backward-char)
|
|
(gas-indent 'forward 'always)
|
|
(let ((curindent (current-indentation)))
|
|
(insert "\n\n")
|
|
(indent-to curindent)
|
|
(insert "*/")
|
|
(forward-line -1)
|
|
(indent-to (+ curindent gas-C-indent))))
|
|
((looking-back "[ \t]*#.*" (line-beginning-position))
|
|
(insert "/* */ ")
|
|
(backward-char 5))
|
|
(t
|
|
(let ((target-col (gas-nth 'text-col 'asm-comment fields)))
|
|
(when target-col
|
|
(move-to-column target-col)))
|
|
(when (or (not subtype)
|
|
(and (<= (current-column) curcol)
|
|
(save-excursion
|
|
(skip-chars-forward (format "%c \t" gas-comment-char))
|
|
(>= (current-column) curcol))))
|
|
(insert-char gas-comment-char 1)
|
|
(setq fields (gas-parsed)))
|
|
(gas-rearrange fields 'text-col 'asm-comment)
|
|
(gas-put-out fields)
|
|
(gas-set-point-to-field 'asm-comment fields))))))
|
|
|
|
(defun gas-fill-paragraph (arg)
|
|
"Beautify asm code block. Within comments, fill current paragraph.
|
|
Calls `fill-paragraph' if within a C comment with ARG passed
|
|
through (which is ignored otherwise)."
|
|
(let ((curcol (current-column))
|
|
(fill-paragraph-function nil)
|
|
; non-empty comment:
|
|
(asm-comment-re (format "\\([ \t]*\\(%c+\\)\\)[ \t]*[^ \t%c\n]\n?"
|
|
gas-comment-char gas-comment-char))
|
|
(empty-line-re "\\([^ \t]+:\\)?[ \t]*$"))
|
|
(cond ((gas-C-comment-really-p)
|
|
(let* ((beg (save-excursion
|
|
(re-search-backward "/#\\|/[*][^C]\\([ \t]*\n\\)?")
|
|
(match-end 0)))
|
|
(end (save-excursion
|
|
(re-search-forward "\\(\n[ \t]*\\)?[*#]/")
|
|
(match-beginning 0))))
|
|
(save-restriction
|
|
(narrow-to-region beg end)
|
|
(fill-paragraph arg))))
|
|
((gas-C-passthrough-code-p)
|
|
(let* ((beg (save-excursion
|
|
(re-search-backward "\\(#/\\|/[*]C\\)\\([ \t]*\n\\)?")
|
|
(match-end 0)))
|
|
(end (save-excursion
|
|
(re-search-forward "\\(\n[ \t]*\\)?[*#]/")
|
|
(match-beginning 0))))
|
|
(goto-char beg)
|
|
(while (< (point) end)
|
|
(let ((fields (gas-parsed)))
|
|
(gas-rearrange fields 'all)
|
|
(gas-put-out fields)
|
|
(beginning-of-line 2)))))
|
|
((save-excursion (beginning-of-line) (looking-at asm-comment-re))
|
|
(let* ((fill-prefix (concat (match-string 1) " "))
|
|
(re (format "[^%c\n]*\\%s[ \t]*[^ \t%c\n]\n?" gas-comment-char
|
|
(match-string 2) gas-comment-char))
|
|
(beg (save-excursion
|
|
(beginning-of-line)
|
|
(while (save-excursion
|
|
(beginning-of-line)
|
|
(looking-at re))
|
|
(end-of-line 0))
|
|
(point)))
|
|
(end (save-excursion
|
|
(end-of-line)
|
|
(while (save-excursion
|
|
(beginning-of-line 2)
|
|
(looking-at re))
|
|
(end-of-line 2))
|
|
(point))))
|
|
(save-restriction
|
|
(narrow-to-region beg end)
|
|
(fill-paragraph arg))))
|
|
(t
|
|
(let ((beg (save-excursion
|
|
(beginning-of-line)
|
|
(while (save-excursion
|
|
(beginning-of-line 0)
|
|
(not (or (looking-at asm-comment-re)
|
|
(looking-at empty-line-re)
|
|
(gas-comment-p))))
|
|
(beginning-of-line 0))
|
|
(point)))
|
|
(end (save-excursion
|
|
(end-of-line)
|
|
(while (save-excursion
|
|
(beginning-of-line 2)
|
|
(not (or (looking-at asm-comment-re)
|
|
(looking-at empty-line-re)
|
|
(gas-comment-p))))
|
|
(end-of-line 2))
|
|
(point))))
|
|
(gas-indent-region beg end))))
|
|
(move-to-column curcol)) t)
|
|
|
|
(defun gas-comment-char ()
|
|
"Handle comment character.
|
|
|
|
If we're in a comment start sequence (either introduced by the
|
|
last command being `gas-comment' or by
|
|
`gas-comment-char-starts-comment' being customized to a non-nil
|
|
value and typing 'gas-comment-char'), increment the level of the
|
|
comment just started.
|
|
|
|
Else, if `gas-comment-char-starts-comment' is customized to a
|
|
non-nil value, start comment.
|
|
|
|
Else just insert variable `gas-comment-char'."
|
|
(interactive)
|
|
(if (and gas-comment-char-starts-comment
|
|
(not (gas-C-comment-p)))
|
|
(gas-comment)
|
|
(if (and gas-doing-comment
|
|
(member last-command '(gas-comment-char gas-comment)))
|
|
(gas-comment)
|
|
(setq gas-doing-comment nil)
|
|
(insert-char gas-comment-char 1))))
|
|
|
|
(provide 'gas-mode)
|
|
|
|
;;; gas-mode.el ends here
|