Skip to content

Add Language detection and dispatch logic#210

Draft
6cdh wants to merge 8 commits intojeapostrophe:masterfrom
6cdh:language
Draft

Add Language detection and dispatch logic#210
6cdh wants to merge 8 commits intojeapostrophe:masterfrom
6cdh:language

Conversation

@6cdh
Copy link
Copy Markdown
Collaborator

@6cdh 6cdh commented Apr 25, 2026

The current LSP server's behavior for non-Racket or non-Lisp languages is unclear. For example, we don't have clear idea what features support what languages. Almost all features target to Racket, but services that operate on expanded code seems more general purpose.

This PR adds language detection and dispatch logic depends on language. And define clear behavior of features for various languages.

TODO:

  • Add a simple token tree parser (no query interface yet)
  • Detect language header with error-tolerance: #lang ... (including #lang reader ...), #reader ..., (module id mod-path ...), if no languag header found, fallback to guess language from special file suffix, like .rhm, .scrbl
  • Maintain a predefined list of languages and their properties. If a recognized language is found in this list, we can apply special logic to them. If a language name is not in this list, it is not treated as a Sexp language, and not a Lisp
  • Stop pass indenter to services, as it's not used
  • Improve diagnostic messages for missing and malformed language header
  • Don't formatting non-Sexp languages
  • Let lexer snapshot stores a token tree, add query interface to token tree, and wire structural queries to use it, instead of traversing and matching parenthesis on flat tokens
  • Allow semantic highlighting to highlight sexp comment #;
  • Clearly define supported languages for each feature
  • Add more languages to predefined list
  • Look at how to handle rktd files

The language header detection is based on the Sexp token tree parser. I didn't use regex because comments can appear before language line, especially Sexp comment. So it builds a small Sexp token tree before the language header. Then recognize the language, if it's in the predefined list, and is a Sexp language, the token tree parser will continue to parse the rest code (continue parsing logic not yet implemented). For non-Sexp language, its Sexp token tree only contains the code before and including the language header. If the language header is missing, the Sexp token tree parser can potentially read the whole document.

The token tree parser can also support non Sexp, but not yet. Because each non Sexp language can have its special syntax, and make the implementation more complex.

6cdh added 3 commits April 22, 2026 19:47
Imported racket-lexer does not return a good meaningful type for some tokens, so add a extra pass to normalize them.
Add doc-lang module for parseing #lang, #reader, and raw module forms, extract language info, and match against a predefined known language list. Fallback to URI suffix based recognization if not found language header.
@6cdh 6cdh changed the title ALanguage Add Language detection and dispatch logic Apr 25, 2026
@6cdh 6cdh marked this pull request as draft April 25, 2026 14:17
Comment thread common/path-util.rkt Outdated
6cdh added 4 commits April 26, 2026 20:20
- Stop pass indenter to services, it's not used by them
- Improve dignostics module
- Skip formatting for not-Sexp languages
- Move indenter lookup into doc-lang.rkt
And make test not require rhombus installed.
Also removed get-indenter test because it returns false now for all builtin languages.
Returns a list of @racket[TextEdit] values to apply, or @racket[#f] if no
indenter is available (e.g., the document lacks a @tt{#lang} line).
Returns a list of @racket[TextEdit] values to apply. For documents without
a recognized s-expression language, returns an empty list.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we only handle s-expression language here? There are actually many non s-expression languages in Racket system.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a problem. We shouldn't route non-Sexp languages to fixw or other Sexp formatters, while almost all formatters in Racket community are Sexp formatters. So we have to have a guard in format logic.

I'm thinking three methods:

  1. Specialize for common non-Sexp languages when we have dedicated formatters for them, for example, scribble or rhombus.
  2. Use the language provided indenter if it provides 'drracket:indentation procedure. But this indent procedure requires a color-textoid<%> argument. Current text buffer does not implement color-textoid<%> interface. And it requires some effort to implement it. My attempt shows it needs another 300 lines of complex code. This is still a good option, if we want best support for various languages. Our old handwritten formatter used this method, but used the text buffer that defined in framework that is a GUI library.
  3. Use method 2, but in a external formatter, so this external formatter becomes a universal indenter for Racket ecosystem. And we just route non S-exp languages to it. We don't route all languages to it because a Sexp formatter usually does more than just indent.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to delay the decision, and avoid to make a color-textoid<%> interface implementation here. AI can do it quickly, but review would be pain.

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.

2 participants