【问题标题】:Do overlays/tooltips work correctly in Emacs for Windows?覆盖/工具提示在 Emacs for Windows 中是否正常工作?
【发布时间】:2019-07-22 12:31:14
【问题描述】:

我在 C# 代码上使用 Flymake,在 Windows 上使用 emacs v22.2.1。

Flymake 的东西对我来说效果很好。对于那些不知道的人,you can read an overview of flymake,但快速的故事是 flymake 在后台重复构建您当前正在处理的源文件,以进行语法检查。然后它突出显示当前缓冲区中的编译器警告和错误。

Flymake 最初不适用于 C#,但 I "monkey-patched it" and it works nicely now。如果你在 emacs 中编辑 C#,我强烈推荐使用 flymake。

我唯一的问题是用户界面。 Flymake 可以很好地突出显示错误和警告,然后插入带有包含完整错误或警告文本的工具提示的“叠加层”。如果我将鼠标指针悬停在代码中突出显示的行上,则会弹出 overlay 工具提示。

但如您所见,overlay 工具提示已被剪裁,无法正确显示。

Flymake 似乎在做正确的事情,似乎是覆盖部分损坏了。,并且覆盖似乎在做正确的事情。它是显示不正确的工具提示。

overlays 工具提示是否在 Windows 的 emacs 中正常工作?

我在哪里可以解决这个问题?


经过一番研究,我发现用(tooltip-show really-long-string)可以证明效果

它与叠加层或 flymake 无关。

【问题讨论】:

  • 看起来像个bug,你可能想M-x report-emacs-bug
  • 在进一步阅读后,我想真正的问题不是覆盖,而是工具提示。
  • 单行工具提示看起来对我来说很好用(例如,将鼠标悬停在缓冲区模式下的缓冲区上)。你的是多线的,我怀疑这是它开始下降的地方
  • 是的,我不知道它是否真的是多行的,或者它只是一个很长的错误信息。但你是对的,多行工具提示不起作用。我通过运行带有嵌入\n 的字符串的tooltip-show 看到了这一点。
  • 有人建议在 Windows 上的 v23.x 中修复了 emacs 中的这个错误。其他人可以确认吗?

标签: c# emacs


【解决方案1】:

我在 tooltip-show 上使用了一个默认建议解决了这个问题。

;; Reforms a single-line string ARG to a multi-line string with a max
;; of LIMIT chars on a line.
;;
;; This is intended to solve a problem with the display of tooltip text
;; in emacs on Win32 - which is that the tooltip is extended to be very very
;; long, and the final line is clipped.
;;
;; The solution is to split the text into multiple lines, and to add a
;; trailing newline to trick the tooltip logic into doing the right thing.
;;
(defun cheeso-reform-string (limit arg)
  (let ((orig arg) (modified "") (curline "") word
        (words (split-string arg " ")))
    (while words
      (progn
        (setq curline "")
        (while (and words (< (length curline) limit))
          (progn
            (setq word (car words))
            (setq words (cdr words))
            (setq curline (concat curline " " word))))
        (setq modified (concat modified curline "\n"))))
    (setq modified (concat modified " \n")))
  )

(defadvice tooltip-show (before
                         flymake-csharp-fixup-tooltip
                         (arg &optional use-echo-area)
                         activate compile)
  (progn
    (if (and (not use-echo-area)
             (eq major-mode 'csharp-mode))
        (let ((orig (ad-get-arg 0)))
          (ad-set-arg 0 (cheeso-reform-string 72 orig))
          ))))

结果:

【讨论】:

    【解决方案2】:

    在摆弄这个 flymake 的东西时,我的真正目标是在 flymake 显示错误时弹出一个“快速修复”选项菜单。如果您单击 ALT-Shift-F10 或类似的东西,Visual Studio 会执行此操作。

    而且,我让它在一些基本场景中发挥作用。 以下是用户体验:

    第 1 步:使用未解析的类型引用编写代码 - 在本例中为 Stream。 Flymake 标记问题,如下所示:

    第二步:通过(flymake-display-err-menu-for-current-line)弹出flymake错误菜单

    第 3 步:选择菜单项,然后自动应用快速修复。


    我安排为一些特殊情况提供“快速修复”选项:

    • 错误 CS0246:找不到类型或命名空间“xxxx”
    • 错误 CS1002:需要分号
    • 错误 CS0103:当前上下文中不存在名称“标识符”。

    再次,诀窍是建议。这次是flymake-make-emacs-menu fn。 flymake 中的那个函数准备了直接传递给x-popup-menu 的数据结构。通知(“后”通知)解析错误列表,查找已知错误代码,如果找到,“猴子补丁”弹出菜单,插入修复错误的选项。

    ;; The flymake-make-emacs-menu function prepares the menu for display in
    ;; x-popup-menu. But the menu from flymake is really just a static list
    ;; of errors.  Clicking on any of the items, does nothing. This advice
    ;; re-jiggers the menu structure to add dynamic actions into the menu,
    ;; for some error cases.  For example, with an unrecognized type error
    ;; (CS0246), this fn injects a submenu item that when clicked, will
    ;; insert a using statement into the top of the file. Other errors are
    ;; also handled.
    ;;
    ;; This won't work generally.  It required some changes to flymake.el,
    ;; so that flymake-goto-next-error would go to the line AND column.  The
    ;; original flymake only goes to the line, not the column.  Therefore,
    ;; quickfixes like inserting a semicolon or a namespace in front of a
    ;; typename, won't work because the position is off.
    ;;
    (defadvice flymake-make-emacs-menu (after
                                        flymake-csharp-offer-quickfix-menu
                                        ()
                                        activate compile)
      (let* ((menu ad-return-value)
             (title (car menu))
             (items (cadr menu))
             action
             new-items
             )
    
        (setq new-items (mapcar
                         '(lambda (x)
                            (let ((msg (car x)) missing-type namespace m2 m3)
                              (cond ((or (string-match "error CS0246:" msg)
                                         (string-match "error CS0103:" msg))
    
                                     (progn
                                       (string-match "^\\(.+'\\([^']+\\)'[^(]+\\)" msg)
                                       (setq missing-type (substring msg
                                                                     (match-beginning 2)
                                                                     (match-end 2)))
    
                                       ;; trim the message to get rid of the (did you forget to ...?)
                                       (setq msg
                                             (substring msg
                                                        (match-beginning 1)
                                                        (match-end 1)))
                                       (setq namespace (csharp-get-namespace-for-type missing-type))
                                       (if namespace
                                           ;; the namespace was found
                                           (progn
                                             (setq m2 (concat "insert   using " namespace ";"))
                                             (setq m3 (concat namespace "." missing-type))
                                             (list msg
                                                   (list m2 'csharp-insert-using-clause-for-type missing-type)
                                                   (list m3 'csharp-insert-fully-qualified-type namespace)
                                                   (list "resolve this type reference manually")))
                                         ;; couldn't find the namespace; maybe it's just a typo
                                         (list msg
                                               (list "resolve this type reference manually")))))
    
                                    ;; missing semicolon
                                    ((string-match "error CS1002:" msg)
                                     (progn
                                         (list msg
                                               (list "insert ; " 'insert ";"))
                                       ))
    
                                    ;; all other cases
                                    (t
                                     ;; no quick fixes for this error
                                     (list msg
                                           (list "resolve this error manually"))))))
                         (cdr items)))
    
        ;; If there's only one menu item, it sometimes won't display
        ;; properly.  The main error message is hidden, and the submenu
        ;; items become the menu items. I don't know why.  Appending a list
        ;; of ("" nil) to the end, insures that the menu will display
        ;; properly.
        (setq new-items (append new-items (list (list "" nil))))
    
        ;; finally, set the return value
        (setq ad-return-value (cons title new-items))
    
        ;; (setq ad-return-value (list title
        ;;                             (list "item1" (list "choice 1.A" 1) (list "choice 1.B" 2))
        ;;                             (list "item2" (list "choice 2.A" 3) (list "choice 2.B" 4))
        ;;                             (list "item3")
        ;;                             ))
    
        ))
    

    “插入使用”修复还取决于查找功能,它将短类型名称(如 Stream)解析为完全限定类型名称,如 System.IO.Stream。这是一个单独的问题。

    如果用户选择菜单项来应用快速修复,它会运行一个 fn 来插入一个新的“using”子句:

    (defun csharp-insert-using-clause (namespace)
      "inserts a new using clause, for the given namespace"
      (interactive "sInsert using clause; Namespace: ")
      (save-excursion
        (let ((beginning-of-last-using (re-search-backward "^[ \t]*using [^ ]+;")))
          (end-of-line)
          (newline)
          (insert (concat "using " namespace ";"))
        )
      )
    )
    

    我认为这可以扩展为处理其他类型错误的快速修复。但我不知道那些易于修复的错误可能是什么。如果有人有任何想法或想提供帮助,请告诉我。

    【讨论】:

    • 如果您要共享 lisp 代码格式,请像 lisp 代码一样。单独在行后面加上 ")" 真的很让人分心,并且让 lisp 代码更难阅读。
    • 这很有趣,它让我更容易阅读。 lisp 真的有标准格式吗?
    猜你喜欢
    • 2014-07-31
    • 1970-01-01
    • 2014-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多