2

When coding Android projects, resource variables are somewhat cumbersome to define, as they involve visiting a file far away in the directory structure and writing xml(!).

Say I want to define a string resource called street. I'll need to:

  1. Visit res/values/strings.xml;
  2. Type an xml entry like <string name="street">VALUE</string>.

If the resource was a number or a list, it goes into another file.

Ideally, this could all be done automatically.

  1. I would write R.string.street in java code (which already have to do anyway);
  2. Then I would just call this magic command;

The command could ask me for the VALUE to use, and it could infer everything else from the R.string.street under point.

Is there a minor-mode or a command that will do this for me?

1

Ok, I ended up programming this myself. I bind it to C-c v.

(defun java/find-or-define-variable ()
  "Go to definition of variable at point. Define it if doesn't exist."
  (interactive)
  (push-mark)
  (unless (or (looking-at "[[:alnum:]_]") (looking-back "[[:alnum:]_]"))    
    (search-forward-regexp "[[:alnum:]_]")
    (forward-char -1))
  (let (name dest file)
    (cond
     ((thing-at-point-looking-at "\\bR\\.\\([[:alnum:]_]+\\)\\.\\([[:alnum:]_]+\\)")
      (if (string= (match-string 1) "id")
          (java/find-id-definition (match-string 2))
        (setq name (match-string 2))
        (setq file (concat (android-root) "res/values/" (match-string 1) ".xml"))
        (unless (file-readable-p file)
          (setq file (concat (android-root) "res/values/" (match-string 1) "s.xml")))
        (find-file file)
        (if (search-forward (format "name=%S" name) nil t)
            (search-forward ">")
          (goto-char (point-max))
          (save-match-data 
            (search-backward-regexp "</\\s-*string>")
            (goto-char (match-end 0)))
          (insert "\n<string name=\"" name "\"></string>")
          (search-backward "</")
          (indent-according-to-mode))))
     (t
      (setq name (thing-at-point 'symbol))
      (setq dest (save-excursion
                   (when (looking-at "[[:alnum:]_]") (forward-sexp 1))
                   (let ((definitionRegexp (concat "\\b[[:alnum:]_]+\\(\\[\\]\\)?\\s-+\\(" name "\\)\\b")))
                     (search-backward-regexp definitionRegexp nil t)
                     (while (save-match-data (looking-at (concat "\\breturn+\\s-+" name "\\b")))
                       (search-backward-regexp definitionRegexp nil t))
                     (match-beginning 2))))
      (if dest
          (goto-char dest)
        (java/previous-end-of-statement)
        (newline) (indent-according-to-mode)
        (insert name ";")
        (back-to-indentation)
        (insert " ")
        (forward-char -1))))))

(defun java/previous-end-of-statement ()
  ""
  (forward-line 0)
  (forward-char -1)
  (when (null (looking-back "[;{]\\s-*\\(//.*\\)?"))
    (forward-line -1)))

(defun java/find-id-definition (id)
  "Used find-grep to look for the \"@+id/ID\" string everywhere."
  (let ((null-device nil))      ; see grep
    (grep-find (format "find %s -type f -exec grep -nH -e 'android:id=\"@+id/%s' {} +" (android-root) id))))
0

You'll have to write some elisp to make this happen. Getting the "R.string.street" and prompting for a value wouldn't be too hard. It might be easier to write a Python script to do the actual edit in the XML file. You would write it as a command line utility and call it from you elisp function that collections the value that you're looking for.

The easiest way to implement the function would be to just prompt for the 3 values that you want, resource-type, resource-name and resource-value and just hand them off to your command line utility.

Of course an elite elisp would probably do the whole operation in elisp but I'm not that comfortable with elisp to do XML parsing.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.