Highlighting correctly in an emacs major mode - emacs

I am developing an emacs major mode for a language (aka mydsl). However, using the techniques on xahlee's site doesn't seem to be working for some reason (possibly older emacs dialect..)
The key issues I am fighting with are (1) highlighting comments is not working and (2), the use of regexp-opt lines is not working.
I've reviewed the GNU manual and looked over cc-mode and elisp mode... those are significantly more complicated than I need.
;;;Standard # to newline comment
;;;Eventually should also have %% to %% multiline block comments
(defun mydsl-comment-dwim (arg)
"comment or uncomment"
(interactive "*P")
(require 'newcomment)
(let
((deactivate-mark nil)
(comment-start "#")
(comment-end "")
comment-dwim arg)))
(defvar mydsl-events
'("reservedword1"
"reservedword2"))
(defvar mydsl-keywords
'("other-keyword" "another-keyword"))
;;Highlight various elements
(setq mydsl-hilite
'(
; stuff between "
("\"\\.\\*\\?" . font-lock-string-face)
; : , ; { } => # $ = are all special elements
(":\\|,\\|;\\|{\\|}\\|=>\\|#\\|$\\|=" . font-lock-keyword-face)
( ,(regexp-opt mydsl-keywords 'words) . font-lock-builtin-face)
( ,(regexp-opt mydsl-events 'words) . font-lock-constant-face)
))
(defvar mydsl-tab-width nil "Width of a tab for MYDSL mode")
(define-derived-mode mydsl-mode fundamental-mode
"MYDSL mode is a major mode for editing MYDSL files"
;Recommended by manual
(kill-all-local-variables)
(setq mode-name "MYDSL script")
(setq font-lock-defaults '((mydsl-hilite)))
(if (null mydsl-tab-width)
(setq tab-width mydsl-tab-width)
(setq tab-width default-tab-width)
)
;Comment definitions
(define-key mydsl-mode-map [remap comment-dwim] 'mydsl-comment-dwim)
(modify-syntax-entry ?# "< b" mydsl-mode-syntax-table)
(modify-syntax-entry ?\n "> b" mydsl-mode-syntax-table)
;;A gnu-correct program will have some sort of hook call here.
)
(provide 'mydsl-mode)

You have a couple of syntactic problems in your code, but you got it nearly correct. Here's my edited version which appears to do the right thing for a buffer in mydsl-mode:
; No changes to the simple vars
(defvar mydsl-events
'("reservedword1"
"reservedword2"))
(defvar mydsl-keywords
'("other-keyword" "another-keyword"))
;; I'd probably put in a default that you want, as opposed to nil
(defvar mydsl-tab-width nil "Width of a tab for MYDSL mode")
;; Two small edits.
;; First is to put an extra set of parens () around the list
;; which is the format that font-lock-defaults wants
;; Second, you used ' (quote) at the outermost level where you wanted ` (backquote)
;; you were very close
(defvar mydsl-font-lock-defaults
`((
;; stuff between "
("\"\\.\\*\\?" . font-lock-string-face)
;; ; : , ; { } => # $ = are all special elements
(":\\|,\\|;\\|{\\|}\\|=>\\|#\\|$\\|=" . font-lock-keyword-face)
( ,(regexp-opt mydsl-keywords 'words) . font-lock-builtin-face)
( ,(regexp-opt mydsl-events 'words) . font-lock-constant-face)
)))
(define-derived-mode mydsl-mode fundamental-mode "MYDSL script"
"MYDSL mode is a major mode for editing MYDSL files"
;; fundamental-mode kills all local variables, no need to do it again
(setq mode-name "MYDSL script")
;; you again used quote when you had '((mydsl-hilite))
;; I just updated the variable to have the proper nesting (as noted above)
;; and use the value directly here
(setq font-lock-defaults mydsl-font-lock-defaults)
;; when there's an override, use it
;; otherwise it gets the default value
(when mydsl-tab-width
(setq tab-width mydsl-tab-width))
;; for comments
;; overriding these vars gets you what (I think) you want
;; they're made buffer local when you set them
(setq comment-start "#")
(setq comment-end "")
(modify-syntax-entry ?# "< b" mydsl-mode-syntax-table)
(modify-syntax-entry ?\n "> b" mydsl-mode-syntax-table)
;;A gnu-correct program will have some sort of hook call here.
)
(provide 'mydsl-mode)

Related

EMACS: How to write a syntax-coloring mode for an Algol-like language that uses keyword stropping

I'm trying to set up syntax colouring for Imp, an Algol-like language that uses keyword stropping, i.e. a keyword is the '%' character followed by alphabetics up to any non-alpha, such as %begin. I found that modify-syntax-entry was sufficient to colour Imp comments, but I had to use Hi-lock to colour keywords. What I'ld like help with is: how can I load the regexps for hi-lock from my ~/.emacs file when opening any file with a .imp extension, rather than having to explicitly save the rules in every individual .imp file? Here's what I have so far:
(defconst my-imp-mode-syntax-table
(let ((table (make-syntax-table)))
(modify-syntax-entry ?' "\"" table)
(modify-syntax-entry ?\" "\"" table)
(modify-syntax-entry ?! "<" table)
(modify-syntax-entry ?\n ">" table)
(modify-syntax-entry ?{ "<" table)
(modify-syntax-entry ?} ">" table)
(modify-syntax-entry ?\( "()" table)
(modify-syntax-entry ?\) ")(" table)
table))
(define-derived-mode my-imp-mode prog-mode "Simple Imp Mode"
:syntax-table my-imp-mode-syntax-table
(font-lock-fontify-buffer))
(add-to-list 'auto-mode-alist '("\\.i\\'" . my-imp-mode))
(add-to-list 'auto-mode-alist '("\\.imp\\'" . my-imp-mode))
(global-hi-lock-mode 1)
(setq hi-lock-file-patterns-policy (lambda (pattern) t))
(defface imp-keyword
'((t (:weight bold :foreground "cyan")))
"Face for IMP keywords"
:group 'hi-lock-faces)
(defface imp-constant
'((t (:foreground "green")))
"Face for IMP numeric constants"
:group 'hi-lock-faces)
;; I would like to load these patterns on opening a .imp file:
;; (("\\%[A-Za-z]*" (0 (quote imp-keyword) prepend)))
;; (("\\<[\\-+]*[0-9]*\\.?[0-9]+\\(\\|#[\\-+]?[0-9]+\\)?\\>" (0 (quote imp-constant) prepend)))
Here is an example of an Imp file:
! Hi-lock: (("\\%[A-Za-z]*" (0 (quote imp-keyword) prepend)))
! Hi-lock: (("\\<[\\-]?[0-9]*\\.?[0-9]+\\(\\#[\\-+]?[0-9]+\\)?\\>" (0 (quote imp-constant) prepend)))
%begin
! ackerman function - this is a whole-line comment
%integer x,y,j,k
%integerfn acker {short for ackerman - this is a bracketed comment by the way} (%integer m,n)
%if m = 0 %then %result = n+1
%if n = 0 %then %result = acker(m-1,1)
%result = acker(m-1, acker(m, n-1))
%end
prompt("Ackerman, First param (1..4)?"); read(x)
prompt(" Second param (1..7)?"); read(y)
write(acker(x,y), 4); newline
%endofprogram
You can see what the expected highlighting looks like by applying the elisp from above. (The details of the colouring are just a draft until I work out how to do it. I can tweak the colours etc later)
The Hi-lock module was not required - it is possible to write syntax colouring for a new language using the standard syntax colouring mechanism, as follows:
(defconst imp-mode-syntax-table
(let ((table (make-syntax-table)))
;; turn off ' and " from being string delimiters
(modify-syntax-entry ?' "-" table)
(modify-syntax-entry ?\" "-" table)
table))
;; Highlight comments, %stropped keywords, and numbers:
(setq imp-highlights
'(
;; many forms of comments including after labels
(";[ ]*!" ".*" nil nil (0 font-lock-comment-face))
("^[ ]*!" ".*;" nil nil (0 font-lock-comment-face))
("^[ ]*!" ".*" nil nil (0 font-lock-comment-face))
("%c\\([ ]+[%]+\\|[%]*\\)*o\\([ ]+[%]+\\|[%]*\\)*m\\([ ]+[%]+\\|[%]*\\)*m\\([ ]+[%]+\\|[%]*\\)*e\\([ ]+[%]+\\|[%]*\\)*n\\([ ]+[%]+\\|[%]*\\)*t" ".*;" nil nil
(0 font-lock-comment-face))
("%c\\([ ]+[%]+\\|[%]*\\)*o\\([ ]+[%]+\\|[%]*\\)*m\\([ ]+[%]+\\|[%]*\\)*m\\([ ]+[%]+\\|[%]*\\)*e\\([ ]+[%]+\\|[%]*\\)*n\\([ ]+[%]+\\|[%]*\\)*t" "[^$]*" nil nil
(0 font-lock-comment-face))
(":[ ]*!" "[^$]*" nil nil (0 font-lock-comment-face))
("{[^}]*}" . 'font-lock-comment-face )
("{.*$" . 'font-lock-comment-face )
;; char constants, old-style strings, and old-style based constants
("[MBOXER]?'[^']*'" . 'font-lock-constant-face)
("\"[^\"]*\"" . 'font-lock-string-face )
;; Based IMP constants
("\\<[0-9][ ]*[0-9 ]*[ ]*_[ ]*[0-9A-Fa-f][0-9A-Fa-f ]*\\>" . 'font-lock-constant-face)
;; Decimal IMP constants
("\\<[0-9][0-9 ]*\\.[ ]*[0-9][0-9 ]*#[ ]*[-+]?[ ]*[0-9][0-9 ]*\\>" . 'font-lock-constant-face)
("\\<[0-9][0-9 ]*\\.[ ]*[0-9][0-9 ]*\\>" . 'font-lock-constant-face)
;; Integer IMP constants
("\\<[0-9][ ]*[0-9][0-9 ]*\\>" . 'font-lock-constant-face)
("\\<[0-9][ ]*\\>" . 'font-lock-constant-face)
;; stropped keywords
("\\%[A-Za-z][A-Za-z]*" . 'font-lock-keyword-face )
))
(define-derived-mode imp-mode prog-mode "Simple Imp Mode"
:syntax-table imp-mode-syntax-table
(setq font-lock-defaults '(imp-highlights))
(font-lock-fontify-buffer)
)
;; Apply automatically to Imp source files:
(add-to-list 'auto-mode-alist '("\\.i\\'" . imp-mode))
(add-to-list 'auto-mode-alist '("\\.imp\\'" . imp-mode))
(add-to-list 'auto-mode-alist '("\\.imp77\\'" . imp-mode))
(add-to-list 'auto-mode-alist '("\\.imp80\\'" . imp-mode))
;; enter <esc>xfun<cr> to disable imp-mode if unwanted during an edit.
Based on the info from http://xahlee.info/emacs/emacs/elisp_syntax_coloring.html

Making an Emacs Derived Mode

Have been using modus-vivendi theme to which I add a bold font for everything for some time.
I would like to introduce a special customisation for comments, using normal weight, and having a keybinding that changes the contrast ratio between the comment colour and the background.
Had started writing a minor mode, but some have suggested the use of a derived mode, as the appropriate way to customise comments as described. Any examples I can use?
In the elisp manual I found define-derived-mode. As the new customisotion is for comments, I would surmise that a closely related mode to inherit would be prog-mode.
But I have no experience on how to go about successfully making a derived mode.
(defvar richkov-annotation-contrast 2
"Sets the colour contrast (against background) for comments.")
(defvar richkov-annotation-chroma
;; Using two Association Lists, each compased of a Chroma Intensity
;; Key (low, mid, high) associated with an RGB Hex-Code. Emacs
;; Minibuffer Command `M-x list-colors-display' lists colour names
;; and hex-codes.
'( (dark . ((low . "#8300E0") ; indigo (Blu:63% Red:37% Grn:0%)
(mid . "#AA33FF") ; indigo (Blu:54% Red:36% Grn:11%)
(high . "#C370FF"))) ; indigo (Blu:45% Red:35% Grn:20%)
(light . ((low . "#C16BFF") ; indigo (Blu:46% Red:35% Grn:19%)
(mid . "#AA33FF") ; indigo (Blu:54% Red:36% Grn:11%)
(high . "#8000DB"))) ) ; indigo (Blu:63% Red:37% Grn:0%)
"Colour contrast for comments, indigo on dark and light background.")
(defun richkov-annotation-font-weight ()
"Use normal weight typeface for comments."
(set-face-attribute 'font-lock-comment-face nil :weight 'normal))
(princ "Weight: ")
(princ (face-attribute 'default :weight))
(defun richkov-annotation-typeface (chroma)
"Set the foreground colour for comments.
CHROMA Intensity Key used for setting colour of comments ."
(message "richkov-annotation-typeface ")
(let* ( (colors richkov-annotation-chroma)
(levels
(alist-get (frame-parameter nil 'background-mode) colors)) )
;; make comment colour change buffer-local
(face-remap-add-relative 'font-lock-comment-face
`(:foreground ,(alist-get chroma levels)))
(message "richkov-annotation: %s contrast" chroma)) )
(defun richkov-annotation-sweep ()
"Cycles through the colour chroma for comments.
Colours are determined by `richkov-annotation'."
(interactive)
(pcase richkov-annotation-contrast
(1
(richkov-annotation-typeface 'low)
(setq richkov-annotation-contrast 2))
(2
(richkov-annotation-typeface 'mid)
(setq richkov-annotation-contrast 3))
(_
(richkov-annotation-typeface 'high)
(setq richkov-annotation-contrast 1)) ))
(defun richkov-annotation-low-contrast ()
(when richkov-minor-mode
(richkov-annotation-typeface 'low)))
(defun richkov-annotation-keytrigger ()
"Key trigger for rapid execution of richkov commands"
(interactive)
(global-set-key (kbd "H-;") #'richkov-annotation-sweep))
(defun richkov-annotation-tools ()
"Aggregates annotation tools for comments."
(richkov-annotation-font-weight)
(richkov-annotation-low-contrast)
(richkov-annotation-keytrigger))
;;;###autoload
(define-minor-mode richkov-minor-mode
"Colour Brace Marks according to their depth."
:lighter "richkov" ; indicator in mode-line
(font-lock-remove-keywords nil richkov-font-lock)
(set-face-attribute 'font-lock-comment-face nil
:weight (face-attribute 'default :weight))
(when richkov-minor-mode
(font-lock-add-keywords nil richkov-font-lock 'append)
(set (make-local-variable 'jit-lock-contextually) t)
(richkov-annotation-tools))
(when font-lock-mode
(if (fboundp 'font-lock-flush)
(font-lock-flush)
(with-no-warnings (font-lock-fontify-buffer)) )) )
;;;###autoload
(defun richkov-minor-mode-enable ()
"Enable `richkov-minor-mode'."
(richkov-minor-mode 1))
;;;###autoload
(defun richkov-minor-mode-disable ()
"Disable `richkov-minor-mode'."
(richkov-minor-mode 0))

Org mode: Problems with folding (no matching item found on current line)

I'm having a weird problem with Emacs (doom-emacs) on macOS (brew cask emacs-mac-spacemacs-icon): I sporadically cannot fold a heading. I can unfold it, then its children are shown, however, when I try to fold it I get the message "No matching item found on the current line". Sometimes it works again after changing to another application and tabbing back, the the message line shows "FOLDED" and "CHILDREN" when I press the Tab key.
It also works in some org files, but not in others. I don't know what's differen between them.
I just realized that when I have an inactive timestamp as a heading, pressing tab once shows the children but pressing it again just jumps between the brackets...
Here is my configuration:
;; Doom exposes five (optional) variables for controlling fonts in Doom. Here
;; are the three important ones:
;;
;; + `doom-font'
;; + `doom-variable-pitch-font'
;; + `doom-big-font' -- used for `doom-big-font-mode'; use this for
;; presentations or streaming.
;;
;; They all accept either a font-spec, font string ("Input Mono-12"), or xlfd
;; font string. You generally only need these two:
(setq doom-font (font-spec :family "Meslo LG M for Powerline"
:size 12))
;; There are two ways to load a theme. Both assume the theme is installed and
;; available. You can either set `doom-theme' or manually load a theme with the
;; `load-theme' function. This is the default:
(setq doom-theme 'doom-gruvbox)
;; Enable visual line mode per default for text files
(add-hook! 'text-mode-hook 'turn-on-visual-line-mode)
;; Enable org-autolist
(add-hook 'org-mode-hook (lambda () (org-autolist-mode)))
;; Org mode configuration
(setq org-directory "~/org")
(setq org-default-clock-file (concat org-directory "/clock.org"))
(after! org
(setq org-agenda-files (list org-directory))
(setq org-default-notes-file (concat org-directory "/inbox.org"))
(setq org-task-file (concat org-directory "/tasks.org"))
(setq org-log-into-drawer t)
(setq org-log-done nil)
(setq org-image-actual-width (/ (display-pixel-width) 3))
(setq org-todo-keywords
'((sequence "TODO(t)" "IN-PROGRESS(i!)" "WAITING(#w)" "|"
"DONE(d!)" "CANCELED(#c)")))
(setq org-todo-keyword-faces
'(
("TODO" . (:foreground "#fb4933" :weight bold))
("IN-PROGRESS" . (:foreground "#fabd2f" :weight bold))
("WAITING" . (:foreground "#fe8019" :weight bold))
("DONE" . (:foreground "#8ec07c" :weight bold))
("CANCELED" . (:foreground "#83a598" :weight bold))
)
)
(setq org-capture-templates
`(("r" "Weekly report" entry (file+headline org-default-clock-file "Clock")
,(concat "** Woche %<%V>\n"
"*** Gesamt\n"
"#+BEGIN: clocktable :scope agenda :maxlevel 20 :block 2020-W%<%V> :step week :stepskip0 t\n"
"#+END:\n"
"*** Tage\n"
"#+BEGIN: clocktable :scope agenda :maxlevel 20 :block 2020-W%<%V> :step day :stepskip0 t\n"
"#+END:"
)
)))
(setq org-startup-indented t)
(setq org-clock-persist 'history)
(org-clock-persistence-insinuate)
(setq org-duration-format (quote h:mm))
(setq org-refile-targets '((org-agenda-files :maxlevel . 10)))
(setq org-goto-interface 'outline-path-completion)
(defvar org-created-property-name "CREATED"
"The name of the org-mode property that stores the creation date of the entry")
(defun org-set-created-property (&optional active NAME)
"Set a property on the entry giving the creation time.
By default the property is called CREATED. If given the `NAME'
argument will be used instead. If the property already exists, it
will not be modified."
(interactive)
(let* ((created (or NAME org-created-property-name))
(fmt (if active "<%s>" "[%s]"))
(now (format fmt (format-time-string "%Y-%m-%d %a %H:%M"))))
(unless (org-entry-get (point) created nil)
(org-set-property created now))))
;; Key mappings
(map! :leader
(:prefix ("o" . "org")
:desc "Goto" "g" 'org-goto
:desc "Insert inactive timestamp" "!" 'org-time-stamp-inactive
:desc "Update dynamic block" "u" 'org-dblock-update
:desc "Todo list" "t" 'org-todo-list
:desc "Agenda" "a" 'org-agenda
:desc "Tag search" "m" 'org-tags-view
:desc "Search org headlines" "h" #'+default/org-notes-headlines
:desc "Search org files" "s" #'+default/org-notes-search
:desc "Refile" "r" 'org-refile
:desc "Browse notes" "f" #'+default/browse-notes
:desc "Search notes for symbol" "." #'+default/search-notes-for-symbol-at-point
:desc "Org capture" "n" #'org-capture
(:prefix ("c" . "clock")
:desc "Clock in" "i" 'org-clock-in
:desc "Clock out" "o" 'org-clock-out
:desc "Jump to last clock" "l" (lambda () (interactive) (setq current-prefix-arg '(4)) (org-clock-goto))
:desc "Jump to active clock" "j" 'org-clock-goto)
(:prefix ("p" . "properties")
:desc "Set CREATED property" "c" 'org-set-created-property)
))
)
;; This determines the style of line numbers in effect. If set to `nil', line
;; numbers are disabled. For relative line numbers, set this to `relative'.
(setq display-line-numbers-type t)
;; Key unmappings
(map! :leader "n" nil)
(map! :leader "o" nil)
i had this problem with doom, due to a keybinding conflict with
evil-jump-item. I fixed it by rebinding tab:
(map! :after evil-org
:map org-mode-map
:desc "org-cycle" :n [tab] #'org-cycle)

define your own tag in org-mode

There are Tags as in #+AUTHOR or #+LATEX in org-mode - are they called tags? I'd like to define my own tag which calls a function to preprocess the data and then outputs it - if the export target is LaTeX.
My solution was defining an own language, qtree, for SRC blocks.
#+BEGIN_SRC qtree
[.CP [.TP [.NP [] [.N' [.N Syntax] []]] [.VP [] [.V' [.V sucks] []]]]]
#+END_SRC
And process it accordingly. I even added a qtree-mode with paredit.
And a landscape parameter if the trees grow big. https://github.com/Tass/emacs-starter-kit/blob/master/vendor/assorted/org-babel-qtree.el
(require 'org)
(defun org-babel-execute:qtree (body params)
"Reformat a block of lisp-edited tree to one tikz-qtree likes."
(let (( tree
(concat "\\begin{tikzpicture}
\\tikzset{every tree node/.style={align=center, anchor=north}}
\\Tree "
(replace-regexp-in-string
" \\_<\\w+\\_>" (lambda (x) (concat "\\\\\\\\" (substring x 1)))
(replace-regexp-in-string
(regexp-quote "]") " ]" ; qtree needs a space
; before every closing
; bracket.
(replace-regexp-in-string
(regexp-quote "[]") "[.{}]" body)) ; empty leaf
; nodes, see
; http://tex.stackexchange.com/questions/75915
) ; For
; http://tex.stackexchange.com/questions/75217
"\n\\end{tikzpicture}"
)))
(if (assoc :landscape params)
(concat "\\begin{landscape}\n" tree "\n\\end{landscape}")
tree)))
(setq org-babel-default-header-args:qtree '((:results . "latex") (:exports . "results")))
(add-to-list 'org-src-lang-modes '("qtree" . qtree))
(define-generic-mode
'qtree-mode ;; name of the mode to create
'("%") ;; comments start with '%'
'() ;; no keywords
'(("[." . 'font-lock-operator) ;; some operators
("]" . 'font-lock-operator))
'() ;; files for which to activate this mode
'(paredit-mode) ;; other functions to call
"A mode for qtree edits" ;; doc string for this mode
)
They seem to be called keywords for in-buffer settings no more. Whatever they're called, they don't seem to be user-definable.
What you want to do is extremely related to a common way of handling whereas to export with xelatex or pdflatex as described on Worg.
The relevant part would be :
;; Originally taken from Bruno Tavernier: http://thread.gmane.org/gmane.emacs.orgmode/31150/focus=31432
(defun my-auto-tex-cmd ()
(if (string-match "YOUR_TAG: value1" (buffer-string))
(do something))
(if (string-match "YOUR_TAG: value2" (buffer-string))
(do something else))
(add-hook 'org-export-latex-after-initial-vars-hook 'my-auto-tex-cmd)

Emacs auto compelete paren, indent and new line - how to?

In C - I want that when I type a { and then } emacs will insert a new line between them and then set the cursor in between them. For example:
int main() {
now I type } and the following happens:
int main()
{
//cursor is here
}
Edit: forgot to mention - I want emacs to know that when defining a function that it should do what was described above but when doing a for loop, or if statement for example I want it to do the following:
if (bla bla) {
type } and... :
if (bla bla) {
//cursor here
}
If you don't mind that the behaviour will be only almost, but not exactly the way you described it, there is a built-in way to do that. It's the auto-newline feature, that can be activated with the key combination C-c C-a or this line your .emacs:
(c-toggle-auto-newline 1)
The difference is that it will do the reformatting right after entering the opening brace {. When you finally enter the closing brace, it will indent it the right way, too.
You also need to set the right CC Mode style. The style "cc-mode" seems to define things the way you described it. You can activate it with the key combination C-c . and then choosing cc-mode, or the .emacs line
(c-set-style "cc-mode")
The c-mode functions are autoloaded and will therefore usually not be available while loading the .emacs file. Therefore you should wrap them in a hook for c-mode, like this
(add-hook 'c-mode-hook
(lambda ()
(c-toggle-auto-newline 1)
(c-set-style "cc-mode")))
As for the { stuff:
(define-minor-mode c-helpers-minor-mode
"This mode contains little helpers for C developement"
nil
""
'(((kbd "{") . insert-c-block-parentheses))
)
(defun insert-c-block-parentheses ()
(interactive)
(insert "{")
(newline)
(newline)
(insert "}")
(indent-for-tab-command)
(previous-line)
(indent-for-tab-command)
)
Paste the above into your .emacs. You can activate it with c-helpers-minor-mode.
Edit: The above inserts everything by just pressing {. The script below should do it if you type {}:
(defun insert-latex-brackets (opening closing) ; prototype function for all enclosing things
(interactive)
(insert opening)
(insert " ")
(insert closing)
(backward-char (+ 1 (length closing )))
)
(defun check-char-and-insert (char opening closing)
(interactive)
(if (equal char (char-to-string (char-before (point))))
(progn (delete-backward-char 1)
(insert-latex-brackets opening closing))
(insert char)
)
)
(local-set-key (kbd "}") 'check-char-and-insert)
One last note: You could try using yasnippet, which can be a real time saver used properly.