Skip to content

Don't fold on tab by default - #2297

Merged
vindarel merged 3 commits into
lem-project:mainfrom
VisenDev:fold
Oct 7, 2026
Merged

vindarel merged 3 commits into
lem-project:mainfrom
VisenDev:fold

Conversation

@VisenDev

Copy link
Copy Markdown
Contributor

Folding function definitions by default whenever you press tab is a new behavior that was just added in this commit about 2 months ago.

While some people might like this behavior, I found it very confusing and it caused friction when trying to use tab for symbol completion and code indentation.

Because the folding behavior is bound to such a fundamental key (Tab) which is already used for several other tasks, I think it would be better for this to be an opt-in feature, rather than enabled by default.

@code-contractor-app

code-contractor-app Bot commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

⏳ Commit 4a3d7f1 has not been validated yet. The results below are for a previous commit. Comment /rerun (keeps dismissed violations dismissed) or @code-contractor-app to re-validate.

✅ Code Contractor Validation: PASSED

📌 Result for commit d1fbc4d

=== Contract: contract ===

✓ Code Contractor Validation Result
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

📋 Contract Source: Repository

📊 Statistics:
  Files Changed:    1
  Lines Added:      1
  Lines Deleted:    1
  Total Changed:    2
  Delete Ratio:     0.50 (50%)

Status: PASSED ✅

🤖 AI Providers:
  - codex — model: (Codex default)

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🎉 No violations detected. Great job!
📋 Contract Configuration: contract (Source: Repository)
version: 2

trigger:
  paths:
    - "extensions/**"
    - "frontends/**/*.lisp"
    - "src/**"
    - "tests/**"
    - "contrib/**"
    - "**/*.asd"
  head_branches:
    exclude:
      - 'revert-*'

validation:
  limits:
    max_total_changed_lines: 400
    max_delete_ratio: 0.5
    max_files_changed: 10
    severity: warning

  ai:
    system_prompt: |
      You are a senior Common Lisp engineer reviewing code for Lem editor.
      Lem is a text editor with multiple frontends (ncurses, SDL2, webview).
      Focus on maintainability, consistency with existing code, and Lem-specific conventions.
    rules:
      # === File Structure ===
      - name: defpackage_rule
        prompt: |
          First form must be `defpackage` or `uiop:define-package`.
          Package name should match filename (e.g., `foo.lisp` → `:lem-ext/foo` or `:lem-foo`).
          Extensions must use `lem-` prefix (e.g., `:lem-python-mode`).

      - name: file_structure_rule
        prompt: |
          File organization (top to bottom):
          1. defpackage
          2. defvar/defparameter declarations
          3. Key bindings (define-key, define-keys)
          4. Class/struct definitions
          5. Functions and commands

          Only flag violations when elements appear OUT OF ORDER (e.g., key bindings AFTER functions, or defvar AFTER functions).
          Key bindings appearing BEFORE functions is correct and expected.

      # === Style ===
      - name: loop_keywords_rule
        prompt: |
          Loop keywords must use colons: `(loop :for x :in list :do ...)`
          NOT: `(loop for x in list do ...)`

      - name: naming_conventions_rule
        prompt: |
          Naming conventions:
          - Functions/variables: kebab-case (e.g., `find-buffer`)
          - Special variables: *earmuffs* (e.g., `*global-keymap*`)
          - Constants: +plus-signs+ (e.g., `+default-tab-size+`)
          - Predicates: -p suffix for functions (e.g., `buffer-modified-p`)
          - Do NOT use -p suffix for user-configurable variables

      # === Documentation ===
      - name: docstring_rule
        prompt: |
          Required docstrings for:
          - Exported functions, methods, classes
          - `define-command` (explain what the command does)
          - Generic functions (`:documentation` option)
          Important functions should explain "why", not just "what".
        severity: warning

      # === Lem-Specific ===
      - name: internal_symbol_rule
        prompt: |
          Use exported symbols from `lem` or `lem-core` package.
          Avoid `lem::internal-symbol` access.
          If internal access is necessary, document why.

      - name: error_handling_rule
        prompt: |
          - `error`: Internal/programming errors
          - `editor-error`: User-facing errors (displayed in echo area)
          Always use `editor-error` for messages shown to users.

      - name: frontend_interface_rule
        prompt: |
          Frontend-specific code must use `lem-if:*` protocol.
          Do not call frontend implementation directly from core.
        severity: warning

      # === Functional Style ===
      - name: functional_style_rule
        prompt: |
          Prefer explicit function arguments over dynamic variables.
          Avoid using `defvar` for state passed between functions.
          Exception: Well-documented cases like `*current-buffer*`.

      - name: dynamic_symbol_call_rule
        prompt: |
          Avoid `uiop:symbol-call`. Rethink architecture instead.
          If unavoidable, document the reason.

      # === Libraries ===
      - name: alexandria_usage_rule
        prompt: |
          Alexandria utilities allowed: `if-let`, `when-let`, `with-gensyms`, etc.
          Avoid: `alexandria:curry` (use explicit lambdas)
          Avoid: `alexandria-2:*` functions not yet used in codebase

      # === Macros ===
      - name: macro_style_rule
        prompt: |
          Keep macros small. For complex logic, use `call-with-*` pattern:
          ```lisp
          (defmacro with-foo (() &body body)
            `(call-with-foo (lambda () ,@body)))
          ```
          Prefer `list` over backquote outside macros.
📚 About Code Contractor

Declarative Code Standards That Learn and Improve

Define domain-specific validation rules in YAML.
Your contracts document team knowledge and evolve into more accurate AI enforcement.

Want this for your repo?
→ Install Code Contractor

@mahmoodsh36

Copy link
Copy Markdown
Contributor

youre probably right, its supposed to fold only when the cursor is on the first line of a defun, but since i dont use completion much i may not have noticed the friction it caused

@VisenDev

Copy link
Copy Markdown
Contributor Author

youre probably right, its supposed to fold only when the cursor is on the first line of a defun, but since i dont use completion much i may not have noticed the friction it caused

Actually maybe its also particularly problematic for me because the folding behavior seems to occur in places where I wanted different behaviors as well.

For example, if I am defining a function, and I want to tab complete a symbol like lem:define-command, and I have my cursor after the f in lem:def and I press Tab, the function just folds, rather than doing symbol completion.

(defun foo () (lem:def))
 ;; CURSOR HERE      ^

Another thing I like to do when working on code is using C-M-u to keep going up a sexpr until my cursor is at the beginning of my function, then doing C-M-Space to select the entire function definition, then pressing Tab to auto-indent the entire function body to align any code blocks which may have ended up improperly indented while editing. In This following example, that workflow no longer works, when I select the body to try and fix the incorrect indentation of the let form, it just folds the function definition when I press tab.

(defun bar ()
  (let ((asdf
     1))
    (print asdf)))

I think if these problems were fixed, I think the folding behavior would be less disruptive. But as it is, it seems to break these tasks

@vindarel

Copy link
Copy Markdown
Collaborator

oh indeed.

Can we bind folding to another key? C-Tab ?

@VisenDev

Copy link
Copy Markdown
Contributor Author

Can we bind folding to another key? C-Tab ?

What is the preferred approach @vindarel @mahmoodsh36 ? Should I disable tab folding by default or change the default folding key so it doesn't conflict with the other tab actions? I think given the problems that the current implementation of the tab folding has that it might be better to use a different key like C-Tab as suggested

@vindarel

Copy link
Copy Markdown
Collaborator

Hey shall you bind folding to C-tab and let's merge this?

I'm annoyed by folding when the cursor is at the end of line too^^ (right before the last paren, where I want code completion)

@vindarel

vindarel commented Oct 5, 2026

Copy link
Copy Markdown
Collaborator

Hi, ping.

@VisenDev

VisenDev commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

Hi, ping.

Sorry I've been busy, I'll see about getting this done

@VisenDev

VisenDev commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

Okay @vindarel C-Tab does not send any input to the editor when running in ncurses mode, so Ctrl-Tab folding would only work on sdl / webview

@vindarel

vindarel commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator

That's a bummer, we need the same behaviour in ncurses.

What about C-c TAB ?

@VisenDev

VisenDev commented Oct 6, 2026

Copy link
Copy Markdown
Contributor Author

Okay I swapped it to C-c Tab

@vindarel
vindarel merged commit 877c683 into lem-project:main Oct 7, 2026
9 of 10 checks passed
@vindarel

vindarel commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator

thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants