LINUX.ORG.RU

Как узнать, под какими дефайнами находится код?

 ,


0

2

Есть ли какой-нибудь способ определить, под какими дефайнами находится код?

Например, если есть что-то такое:

#ifndef X
void foo( bool b)
{
#if (Y == Z)
    baz();    <=== здесь хочется получить что-то вроде "ifndef X && (Y == Z)"
#endif
}
#else
void foo()
{
    bar(); <=== а здесь "!ifndef X"
}
#endif

Задача похожа на поиск парных скобок, и без автоматизации жутко раздражает.



Последнее исправление: j-a-t-a (всего исправлений: 1)

И не только KDevelop. QtCreator, Eclipse, etc..

solovey ★★
()
Ответ на: комментарий от helios

Эй. Ты с ума сошёл?! Аккуратней надо.
Ты только что сообщил пользователю емакса, что емакс что-то не умеет.
А если он не стрессоустойчивый? Наложит на себя руки — тебе приятно будет?

Для ТС по теме:
Сам емакс не осилил, но там же можно на лиспе расширения писать... Напиши:)

Stahl ★★☆
()
Ответ на: комментарий от helios

ifdef.vim — не для этого, насколько я понял. Мне нужна информация не о определенности символа, а о том, под какой веткой дерева дефайнов находится указанная строка. Для этого даже не надо знать, какие дефайны определены. С точки зрения интерфейса идея похожа на отображение заголовка функции в любом месте ее кода из кедета. Расширение — ок, напишу.

j-a-t-a
() автор топика
Ответ на: комментарий от nikitos

Нет, мне не нужен препроцессированный код. Мне нужно знать, под какими дефайнами написан данный кусок кода. Вообще это и глазками можно определить, но искомый #ifdef может быть парой тысяч строк кода выше. Собственно, человеческая мотивация такова — я изменил интерфейс функции, поправил все на что указал компилятор (храни бог создателей статической типизации), а потом нашел кусок кода, в котором используется старый интерфейс, и на который компилятор не ругается. Потом долго разбирался почему, а оказалось что тысячей строк выше стоял #ifdef, выбирающий компилируемый вариант функции. #ifdef этот я искал долго, потому что их дохрена, но в основном они ограничивают очень маленькие куски кода. Вот эту задачу и хочется автоматизировать.

j-a-t-a
() автор топика
Ответ на: комментарий от helios

Так бы я ещё написал, что у vim'а есть ifdef.vim для этого :-D

Который работает только с простыми конструкциями. Бесполезный плагин, на мой взгляд. Хотя сносить его лень :)

andreyu ★★★★★
()
Ответ на: комментарий от j-a-t-a

Вообще это и глазками можно определить, но искомый #ifdef может быть парой тысяч строк кода выше.

В виме можно скакать с помощью %

andreyu ★★★★★
()
Ответ на: комментарий от Stahl

В емакс лиспе я не силен, так что решение смешанное. Зато его можно использовать и в вашем поделии, если уж ifdef.vim не хватит Написал перловый скриптик:

#!/usr/bin/perl
# Usage: file_name line_number

my $fileName = shift;
my $lineLimit = shift;

my $curLineNum = 0;
my @curDefines;

open( IN, $fileName);
while (<IN>)
{
    $curLineNum++;
    last if ($curLineNum > $lineLimit);
    
    if (/#(ifdef|ifndef|if)+?/)
    {
        push(@curDefines, "$curLineNum: $_");
    }
    elsif (/\#else/)
    {
        $lastDefine = pop(@curDefines);
        $lastDefine =~ s/^\d+:/$curLineNum: !/;
        push(@curDefines, $lastDefine);
    }
    elsif (/\#endif/)
    {
        pop(@curDefines);
    }
}

foreach $define (@curDefines)
{
    print "$define\n";
}

Он получает на вход имя файла и номер строки, к которой надо собрать путь по дереву дефайнов. Пример выдачи:

$ ./define_branch.pl foo.cpp 5870
3602: ! #if defined(PERF) && defined(PERF_SNR)
4700: #ifndef FOO

Чтобы удобнее было использовать из емакса, делаем команду:

(defun get-defines-branch ()
  "Print path in define tree to current line in current buffer"
  (interactive)
  (shell-command (format "perl <путь к скрипту>/define_branch.pl %s %d" 
                         (buffer-file-name) 
                         (count-lines (point-min) (point)))))

j-a-t-a
() автор топика
Ответ на: комментарий от j-a-t-a

См. функции c-*-conditional. Вот грубый набросок:

(defun pp-stack ()
  (interactive)
  (setq stack nil)
  (ignore-errors
    (save-excursion
      (loop
       (c-up-conditional 1)
       (setq line (buffer-substring (point-at-bol) (point-at-eol)))
       (set-text-properties 0 (length line) nil line)
       (add-to-list 'stack line))))
  (with-output-to-temp-buffer "*pp-stack*"
    (dolist (e stack)
      (print e))))
gv
()
Ответ на: комментарий от gv

Крутейше, спасибо! Только две детали, которые есть только в скрипте: номера строк и отрицание дефайна, если код находится под #else. А так очень круто!

j-a-t-a
() автор топика
Ответ на: комментарий от j-a-t-a

Номер строки можно узнать с помощью line-number-at-pos. Чтобы прыгнуть на #else, нужно вместо c-up-conditional использовать c-up-conditional-with-else.

Еще не обрабатываются многострочные #if (с бэк-слешом в конце).

gv
()
Ответ на: комментарий от gv

Немного допилил, добавил номера строк и указание условия для else-веток:

(defun print-define-path ()
  "Print path through define tree to current line"
  (interactive)
  (setq stack nil)
  (ignore-errors
    (save-excursion
      (loop
       (c-up-conditional-with-else 1)
       (setq line (buffer-substring (point-at-bol) (point-at-eol)))
       (setq line-num (line-number-at-pos))
       (if (string-match line "#else") ;; if it's 'else' branch, save condition from 'if' branch with ! and 'else' line num
           (progn
             (c-up-conditional 1)
             (setq line (buffer-substring (point-at-bol) (point-at-eol)))
             (setq line (concat "! " line))))
       (set-text-properties 0 (length line) nil line)
       (add-to-list 'stack (format "%d: %s" line-num line)))))
  (with-output-to-temp-buffer "*pp-stack*"
    (dolist (e stack)
      (print e))))
Работает хорошо, а многострочными дефайнами можно пренебречь :)

j-a-t-a
() автор топика
Ответ на: комментарий от j-a-t-a

Спасибо.
Отрефакторил, убрал string-match "#else" (это не всегда будет работать), добавил многострочные комментарии и сохранение подсветки.

Для счастья не хватает вынести номера строк в отдельную область (как в linum-mode) и сделать строчки кликабельными.

(defun get-conditional-text (pos)
  (save-excursion
    (goto-char pos)
    (while (looking-at "^[^\n]*\\\\\\s-*$")
      (next-line))
    (next-line)
    (buffer-substring pos (point))))

(defun get-upper-conditional ()
  (condition-case nil
      (let ((if-pos 0)
            (else-pos 0))
        (save-excursion
          (c-up-conditional 1)
          (setq if-pos (point)))
        (save-excursion
          (c-up-conditional-with-else 1)
          (setq else-pos (point)))
        (if (= if-pos else-pos)
            (format "%d: %s"
                    (count-lines 1 if-pos)
                    (get-conditional-text if-pos))
          (format "%d: ! %s"
                  (count-lines 1 else-pos)
                  (get-conditional-text if-pos))))
    (error nil)))

(defun print-define-path ()
  "Print path through define tree to current line"
  (interactive)
  (let (pp-stack)
    (save-excursion
      (while (setq cond (get-upper-conditional))
        (add-to-list 'pp-stack cond)
        (c-up-conditional 1)))
   (with-output-to-temp-buffer "*pp-stack*"
     (with-current-buffer standard-output
       (dolist (e pp-stack)
         (insert e))))))
gv
()
Ответ на: комментарий от gv

s/многострочные комментарии/многострочные дефайны/

gv
()
Ответ на: комментарий от j-a-t-a

Для счастья не хватает вынести номера строк в отдельную область (как в linum-mode) и сделать строчки кликабельными.

Upgrade:

(defface defpath-linum-face
  '((t (:foreground "gray30"))) nil)

(defun defpath--linum-format-string ()
  (let* ((lines
          (count-lines 1 (point-max)))
         (digits
          (max 3 (length (format "%d" lines)))))
    (concat " %0" (format "%d" digits) "d ")))

(defun defpath--get-conditional-at-pos (pos)
  (save-excursion
    (goto-char pos)
    (while (looking-at "^[^\n]*\\\\\\s-*$")
      (next-line))
    (next-line)
    (buffer-substring pos (point))))

(defun defpath--upper-conditional ()
  (condition-case nil
      (let ((if-pos
             (save-excursion
               (c-up-conditional 1)
               (setq if-pos (point))))
            (else-pos
             (save-excursion
               (c-up-conditional-with-else 1)
               (setq else-pos (point)))))
        (if (= if-pos else-pos)
            (cons
             (1+ (count-lines 1 if-pos))
             (defpath--get-conditional-at-pos if-pos))
          (cons
           (1+ (count-lines 1 else-pos))
           (format "! %s" (defpath--get-conditional-at-pos if-pos)))))
    (error nil)))

(defun defpath--traverse ()
  (let (stack)
    (save-excursion
      (while (setq conditional (defpath--upper-conditional))
        (add-to-list 'stack conditional)
        (c-up-conditional 1)))
    stack))

(defun defpath--setup-reference (start end buffer line)
  (let ((map
         (make-sparse-keymap)))
    (dolist (key
             `(,(kbd "<mouse-1>")
               ,(kbd "RET")))
      (define-key map key
        `(lambda ()
           (interactive)
           (pop-to-buffer ,buffer)
           (goto-line ,line))))
    (add-text-properties
     start end
     `(mouse-face highlight
       help-echo "<mouse-1> or RET: visit this line in original buffer"
       keymap ,map))))

(defun defpath--setup-line-number (line fstr)
  (let ((string
         (format fstr line))
        (overlay
         (make-overlay (point) (1+ (point)))))
    (overlay-put overlay 'line-number t)
    (overlay-put overlay 'evaporate t)
    (overlay-put overlay 'before-string
                 (propertize " " 'display
                             `((margin left-margin)
                               ,(propertize string 'face 'defpath-linum-face))))))

(defun defpath--insert-conditional (info fstr buffer)
  (let* ((line (car info))
         (text (cdr info))
         (start (point))
         (end
          (save-excursion
            (insert text)
            (1- (point)))))
    (defpath--setup-line-number line fstr)
    (defpath--setup-reference start end buffer line)
    (goto-char (point-max))))

(defun defpath ()
  "Print path through define tree to current line"
  (interactive)
  (let ((stack
         (defpath--traverse))
        (fstr
         (defpath--linum-format-string))
        (buffer
         (current-buffer)))
    (with-output-to-temp-buffer "*defpath*"
      (with-current-buffer standard-output
        (dolist (conditional stack)
          (defpath--insert-conditional conditional fstr buffer))))
    (let ((width
           (length (format fstr (line-number-at-pos)))))
      (set-window-margins
       (get-buffer-window "*defpath*") width))))

Теперь можно куда-нибудь выложить, но мне лень.

gv
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.