Emacs: разные отступы для класса и структуры

Я пытаюсь выполнить следующий отступ в emacs:

class A
{
    // I ALWAYS use access labels in classes

    public: // access-label
        int member; // inclass
};

struct B
{
    // I NEVER use access labels in structs

    int member; // inclass
};

Однако со следующим конфигурационным файлом...

(defun my-cpp-mode ()
  "My C++ mode"
  (c++-mode)
  (c-set-style "K&R")
  (setq c-basic-offset 4)
  (c-set-offset 'access-label '-)
  (c-set-offset 'inclass '++)
  ;; ...
  (setq mode-name "My C++")
)
(add-to-list 'auto-mode-alist '("\\.[ch]p?p?\\'" . my-cpp-mode))

... Я достигаю только:

class A
{
    public: // access-label
        int member; // inclass
};

struct B
{
        // this indentation is too long
        int member; // inclass
};

Конечно, потому что:

  • для отступа, очевидно, нет разницы между "классом" и "структурой" (все это "класс" ),
  • отступы данных "inclass" не зависят от наличия меток доступа или нет.

Любая идея о том, как я могу сделать отступы в классе inclass, зависящем либо от класса/структуры, либо от наличия меток доступа?

Ответ 1

Новый ответ

Я столкнулся с точным требованием, которое вы упомянули в своем вопросе. Мне пришлось настраивать отступы в соответствии со стилем кодирования моего нового проекта. После небольшого исследования я достиг этого с помощью Custom Line-up Functions.

Измените свой my-cpp-mode, чтобы он выглядел следующим образом:

(defun my-c-lineup-inclass (langelem)
  (let ((inclass (assoc 'inclass c-syntactic-context)))
    (save-excursion
      (goto-char (c-langelem-pos inclass))
      (if (or (looking-at "struct")
              (looking-at "typedef struct"))
          '+
        '++))))

(defun my-cpp-mode ()
  "My C++ mode"
  (c++-mode)
  (c-set-style "K&R")
  (setq c-basic-offset 4)
  (c-set-offset 'access-label '-)
  (c-set-offset 'inclass 'my-c-lineup-inclass)
  ;; ...
  (setq mode-name "My C++")
)

Если этот ответ является приемлемым, я продолжу и удалю старый ответ.

Старый ответ

На основе того, что вы пытаетесь достичь, могу ли я предложить другой подход? Кажется, вам нужна метка доступа на другом уровне отступов, чем класс и члены класса. Для этого используйте следующее.

(access-label . /)

Из документации Emacs:

Если OFFSET является одним из символов +', - ', ++', -', *', or/' то положительный или отрицательный кратный "c-basic-offset" добавляется к базовый отступ; 1, -1, 2, -2, 0,5 и -0,5 соответственно.

Вот фрагмент из одного из настраиваемых стилей, которые я определил.

(c-add-style
 "xyz-style"
 '((indent-tabs-mode . nil)
   (fill-column . 75)
   (c-basic-offset . 4)
   (c-offsets-alist . (
                       (access-label . /)
                       (inextern-lang . 0)
                       (innamespace . 0)
                       (member-init-intro . ++)
                       ))))

С c-basic-offset, установленным в 4, (access-label . /) добавляет отрицательный отступ в 2 пробела к меткам доступа. Вот фактический результат моего режима отступов в вашем примере кода.

class A
{
    // I ALWAYS use access labels in classes

  public: // access-label
    int member; // inclass
};

struct B
{
    // I NEVER use access labels in structs

    int member; // inclass
};

Я рекомендую этот режим, потому что уровень отступов членов-членов-членов-членов согласован. FWIW, Google C Style следует тому же подходу.

Насколько я могу судить, нельзя различать член класса или член структуры (inclass sytax element). Вы можете использовать M-x c-syntactic-information-on-region для синтаксического анализа в регионе. Один такой анализ на вашем примере дает следующее. Из вывода нечего отличать, если вы находитесь в классе или структуре.

class A                                 // ((topmost-intro 1))
{                                       // ((class-open 1))
                                        // ((inclass 64) (topmost-intro 64) (comment-intro))I ALWAYS use access labels in classes
                                        // ((inclass 64) (topmost-intro 64))
  public:                               // ((inclass 64) (access-label 64))access-label
    int member;                         // ((inclass 64) (topmost-intro 64))inclass
};                                      // ((class-close 1))
                                        // ((topmost-intro 503))
struct B                                // ((topmost-intro 503))
{                                       // ((class-open 629))
                                        // ((inclass 694) (topmost-intro 694) (comment-intro))I NEVER use access labels in structs
                                        // ((inclass 694) (topmost-intro 694))
    int member;                         // ((inclass 694) (topmost-intro 694))inclass
};                                      // ((class-close 629))

Ответ 2

Основываясь на ответе Правэва Кумара выше, я внедрил немного другую версию настраиваемой функции:

(defun my-c-lineup-inclass (langelem)
  (let ((inclass (assoc 'inclass c-syntactic-context)))
    (save-excursion
      (c-beginning-of-defun) ; This sees the correct string.
      (if (or (looking-at "struct")
              (looking-at "typedef struct"))
          '+
        '++))))

; In particular, the following offsets need to be specified:
(c-set-offset 'access-label '-)
(c-set-offset 'inclass 'my-c-lineup-inclass)
; ...

Исходный код не работал, если скобка была на следующей строке, т.е.

struct foo
{
        int bar;
};

все равно будет отступать до "++".

Отказ от ответственности: я не знаю никаких Lisp. Я просто играл, и это работает для меня. Я не знаю, например, если есть какие-либо проблемы с производительностью, связанные с этим.

Ответ 3

Стандарты кодирования моего проекта теперь требуют такого отступов. Как и Аркадий, я предпочитаю решение, которое уважает метки доступа. Вот что я придумал:

(defun indent-after-access-label (langelem)
  "Return the appropriate indent for a class or a struct."
  (save-excursion
    (save-match-data
      ;; Optimization to avoid at least a few calls to re-search-backward.
      (if (assoc 'access-label c-syntactic-context)
          '++
        (if (re-search-backward "\\(?:p\\(?:ublic\\|r\\(?:otected\\|ivate\\)\\)\\)" c-langelem-pos langelem) t)
            '++
          '+)))))

Как уже упоминалось ранее, символ indent-after-acess-label должен быть установлен как отступ для inclass (через c-set-offset или c-offset-alist и т.д.).

Это вряд ли идеально, из-за re-search-backward, но он работает.