emacsorphanage / dart-mode

An Emacs mode for the Dart language
GNU General Public License v3.0
15 stars 2 forks source link

Conflicted Key Bindings (C-c TAB and C-c C-i) #112

Closed gkmngrgn closed 4 years ago

gkmngrgn commented 4 years ago

Hi,

I'm using the key binding C-c TAB for tab completion in all modes but seems dart-mode is overwriting one of my key bindings:

^L
Major Mode Bindings Starting With C-c:
key             binding
---             -------
C-c TAB         indent-according-to-mode

In ASCII, C-c C-i and C-c TAB are the same characters so the tab completion doesn't work on dart-mode: https://github.com/bradyt/dart-mode/blob/master/dart-mode.el#L22

In ASCII, C-i and \ are the same character. If the terminal can distinguish between them, Emacs conveys the distinction to Lisp programs by representing the former as the integer 9, and the latter as the symbol tab.

According this document, C-c... shortcuts are reserved for the users. So it may be a good idea to remove dart-mode-map and add a short info to README.md.

bradyt commented 4 years ago

Thank you for the bug report. I will try to make time to give this a careful read, potentially within a couple of days, or hopefully at least within a week.

bradyt commented 4 years ago

The quickest fix for users, at the moment, might be the following,

(with-eval-after-load "dart-mode"
  (define-key dart-mode-map (kbd "C-c C-i") nil))

This is just meant as a quick temporary solution/reply.

I need to research more, for better solutions.

bradyt commented 4 years ago

According to that documentation, key binding conventions suggest that C-c <letter> are reserved for users. This does not include C-c C-i.

  • Don't define C-c letter as a key in Lisp programs. Sequences consisting of C-c and a letter (either upper or lower case) are reserved for users; they are the only sequences reserved for users, so do not block them.

...

  • Sequences consisting of C-c followed by a control character or a digit are reserved for major modes.

This binding was partially motivated by how bad the indentation function is in dart-mode. I think if we can get the indentation function to work with statements continued on newlines, we could revisit which bindings to have and where. I originally tried to use the approach of the indentation function in go-mode.el, but haven't figured that out yet.

By the way... I don't know if you are using terminal emacs or GUI emacs. In GUI emacs, I can achieve distinct results between pressing Tab versus Ctrl+i. I could at least, but I don't actually do this.

You may find these properties interesting:

(equal (kbd "TAB") (kbd "C-i"))         ; t
(equal (kbd "<tab>") (kbd "C-i"))       ; nil
(string-to-char (kbd "TAB"))            ; 9 (#o11, #x9, ?\C-i)

So in GUI Emacs, I see Tab behaves as <tab>, and Ctrl+i behaves as TAB.

You can experiment with the following example.

(global-set-key (kbd "TAB") (lambda () (interactive) (message "alice")))
(global-set-key (kbd "<tab>") (lambda () (interactive) (message "bob")))

However, in terminal Emacs, I see Tab behaves as TAB.

In case further clarification should occur, I wasn't clear what you meant by this.

So it may be a good idea to remove dart-mode-map and add a short info to README.md.

gkmngrgn commented 4 years ago

Hi @bradyt,

First, thank you for all your effort. Indeed, I already found my solution and you can see here, it's doing the similar thing:

(use-package dart-mode
  :defer t
  :hook (dart-mode . (lambda() (local-unset-key (kbd "C-c C-i")))))

So in GUI Emacs, I see Tab behaves as \, and Ctrl+i behaves as TAB.

Oh, I didn't know that. I use Emacs both with GUI and Terminal. For now, I decided to unset the key to dart-mode. Even I don't use indent-according-to-mode actively because lsp-mode does already fix the indentation as well.

In case further clarification should occur, I wasn't clear what you meant by this.

Sure, let me show you an example from projectile:

(define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)

Projectile has many shortcuts but it leaves me the choice of how I will map all the keys. It's not defined in the projectile mode inside but I need to add this line to my init.el. You can see the same logic in lsp-mode:

(setq lsp-keymap-prefix "C-c l")
bradyt commented 4 years ago

Sounds good, thanks for the additional notes here.

With the point above about C-c <letter> versus C-c followed by a control character, is this issue resolved?

gkmngrgn commented 4 years ago

@bradyt yes this solves my problem:

(use-package dart-mode
  :defer t
  :config
  (define-key dart-mode-map (kbd "C-c C-i") nil))

But I think the approach of Projectile is a bit better because it leaves me the choice. Dart-mode is setting the key binding without asking me. Instead, that would be a nice if we map all dart-mode specific keys with a line, for example:

(use-package dart-mode
  :defer t
  :config
  (define-key dart-mode-map (kbd "C-c d") 'dart-command-key)
  ;; C-c d TAB     : runs indent-according-to-mode
  ;; C-c d BACKTAB : runs dart-dedent-simple
  ;; C-c d f       : runs dartfmt (if you want to add more keys)
  ;; C-c d p       : runs pub get
  )

Thus keymap conflicts are avoided—just an idea. If you don't think of making any change, your first solution is ok for me. Or if you agree with my opinion, I can open a pull-request for that.

bradyt commented 4 years ago

This all seems addressed by https://www.gnu.org/software/emacs/manual/html_node/elisp/Key-Binding-Conventions.html.

The examples you provide from projectile and lsp-mode are consistent with C-c <letter>.

The binding here for C-c C-i is consistent with C-c and a control character being reserved for major-modes.

You will find C-c C-i bound in many major modes.

bradyt commented 4 years ago

I think I missed the operative part there for me, "yes this solves my problem". I think I can close this for now, additional comments welcome. I hope I gave sufficient reason.