···11-pub fn add(left: u64, right: u64) -> u64 {
22- left + right
33-}
44-55-#[cfg(test)]
66-mod tests {
77- use super::*;
88-99- #[test]
1010- fn it_works() {
1111- let result = add(2, 2);
1212- assert_eq!(result, 4);
1313- }
1414-}
+110
mlf-diagnostics/src/lib.rs
···216216 name, namespace_suffix
217217 )
218218 }
219219+ ValidationError::CircularImport { cycle, .. } => {
220220+ write!(f, "Circular import detected: {}", cycle.join(" → "))
221221+ }
222222+ ValidationError::UnusedImport { name, .. } => {
223223+ write!(f, "Unused import '{}'", name)
224224+ }
219225 }
220226}
221227···230236 ValidationError::AmbiguousMain { .. } => "mlf::ambiguous_main",
231237 ValidationError::MultipleMain { .. } => "mlf::multiple_main",
232238 ValidationError::ConflictNotAllowed { .. } => "mlf::conflict_not_allowed",
239239+ ValidationError::CircularImport { .. } => "mlf::circular_import",
240240+ ValidationError::UnusedImport { .. } => "mlf::unused_import",
233241 }
234242}
235243···301309 span.start..span.end,
302310 format!("'{}' conflicts with another definition", name),
303311 )],
312312+ ValidationError::CircularImport { span, cycle, .. } => vec![LabeledSpan::at(
313313+ span.start..span.end,
314314+ format!("Import creates a cycle: {}", cycle.join(" → ")),
315315+ )],
316316+ ValidationError::UnusedImport { span, name, .. } => vec![LabeledSpan::at(
317317+ span.start..span.end,
318318+ format!("Import '{}' is never used", name),
319319+ )],
304320 }
305321}
306322···331347 "Numeric constraints (minimum, maximum) can only be applied to integer or number types.",
332348 ))
333349 }
350350+ ValidationError::InvalidConstraint { message, .. }
351351+ if message.contains("Blob constraint on non-blob") =>
352352+ {
353353+ Some(Box::new(
354354+ "Blob constraints (accept, maxSize) can only be applied to blob types.",
355355+ ))
356356+ }
357357+ ValidationError::InvalidConstraint { message, .. }
358358+ if message.contains("Length constraint") =>
359359+ {
360360+ Some(Box::new(
361361+ "Length constraints (minLength, maxLength) can be applied to strings or arrays.",
362362+ ))
363363+ }
364364+ ValidationError::InvalidConstraint { message, .. }
365365+ if message.contains("Union type must have at least one member") =>
366366+ {
367367+ Some(Box::new(
368368+ "Union types must contain at least one type. Add a type to the union or use a different type.",
369369+ ))
370370+ }
334371 ValidationError::ConstraintTooPermissive { message, .. } if message.contains("maxLength") => {
335372 Some(Box::new(
336373 "When refining a constrained type, maxLength can only decrease (become more restrictive).",
···341378 "When refining a constrained type, minLength can only increase (become more restrictive).",
342379 ))
343380 }
381381+ ValidationError::ConstraintTooPermissive { message, .. } if message.contains("maximum") => {
382382+ Some(Box::new(
383383+ "When refining a constrained type, maximum can only decrease (become more restrictive).",
384384+ ))
385385+ }
386386+ ValidationError::ConstraintTooPermissive { message, .. } if message.contains("minimum") => {
387387+ Some(Box::new(
388388+ "When refining a constrained type, minimum can only increase (become more restrictive).",
389389+ ))
390390+ }
391391+ ValidationError::ConstraintTooPermissive { message, .. } if message.contains("maxGraphemes") => {
392392+ Some(Box::new(
393393+ "When refining a constrained type, maxGraphemes can only decrease (become more restrictive).",
394394+ ))
395395+ }
396396+ ValidationError::ConstraintTooPermissive { message, .. } if message.contains("minGraphemes") => {
397397+ Some(Box::new(
398398+ "When refining a constrained type, minGraphemes can only increase (become more restrictive).",
399399+ ))
400400+ }
401401+ ValidationError::ConstraintTooPermissive { message, .. } if message.contains("maxSize") => {
402402+ Some(Box::new(
403403+ "When refining a constrained type, maxSize can only decrease (become more restrictive).",
404404+ ))
405405+ }
406406+ ValidationError::ConstraintTooPermissive { message, .. } if message.contains("enum") => {
407407+ Some(Box::new(
408408+ "When refining a constrained type, enum values must be a subset of the base enum.",
409409+ ))
410410+ }
411411+ ValidationError::DuplicateDefinition { .. } => {
412412+ Some(Box::new(
413413+ "Each name can only be defined once in a module. Consider renaming one of the items or using @main annotation if they match the namespace suffix.",
414414+ ))
415415+ }
416416+ ValidationError::ReservedName { name, .. } if name == "main" => {
417417+ Some(Box::new(
418418+ "The name 'main' is reserved and cannot be used as an item name. Use @main annotation on an item instead.",
419419+ ))
420420+ }
421421+ ValidationError::ReservedName { name, .. } if name == "defs" => {
422422+ Some(Box::new(
423423+ "The name 'defs' is reserved for future use and cannot be used as an item name.",
424424+ ))
425425+ }
426426+ ValidationError::AmbiguousMain { .. } => {
427427+ Some(Box::new(
428428+ "When multiple items have the same name as the namespace suffix, use @main to mark which one is the primary definition.",
429429+ ))
430430+ }
431431+ ValidationError::MultipleMain { .. } => {
432432+ Some(Box::new(
433433+ "Only one item can be marked with @main. Remove the @main annotation from all but one item.",
434434+ ))
435435+ }
436436+ ValidationError::ConflictNotAllowed { namespace_suffix, .. } => {
437437+ Some(Box::new(format!(
438438+ "Name conflicts are only allowed when the item name matches the namespace suffix ('{}').",
439439+ namespace_suffix
440440+ )))
441441+ }
442442+ ValidationError::CircularImport { .. } => {
443443+ Some(Box::new(
444444+ "Circular imports are not allowed. Reorganize your modules to break the cycle.",
445445+ ))
446446+ }
447447+ ValidationError::UnusedImport { .. } => {
448448+ Some(Box::new(
449449+ "This import is never used. Consider removing it to keep the code clean.",
450450+ ))
451451+ }
344452 _ => None,
345453 }
346454}
···356464 ValidationError::AmbiguousMain { module_namespace, .. } => module_namespace,
357465 ValidationError::MultipleMain { module_namespace, .. } => module_namespace,
358466 ValidationError::ConflictNotAllowed { module_namespace, .. } => module_namespace,
467467+ ValidationError::CircularImport { module_namespace, .. } => module_namespace,
468468+ ValidationError::UnusedImport { module_namespace, .. } => module_namespace,
359469 }
360470}
361471
···11+# mlf-lsp
22+33+Language Server Protocol (LSP) implementation for Matt's Lexicon Format (MLF).
44+55+## Features
66+77+### Currently Implemented
88+99+- **Diagnostics**: Real-time syntax error reporting as you type
1010+- **Document Synchronization**: Tracks open/changed/closed MLF files
1111+- **Hover Information**: Shows type information, documentation comments, and field details on hover
1212+- **Completion**: Auto-complete for keywords, primitive types, defined types, and constraint names
1313+- **Go to Definition**: Jump to type definitions across files using workspace resolution
1414+- **Workspace Support**: Automatically builds a workspace from open files with cross-file type resolution
1515+- **Logging**: Server activity logging for debugging
1616+1717+### Planned Features
1818+1919+- **Formatting**: Auto-format MLF code from AST
2020+- **Rename**: Rename symbols across files
2121+- **Find References**: Find all usages of a symbol
2222+- **Code Actions**: Quick fixes and refactoring
2323+- **Signature Help**: Parameter hints for queries/procedures
2424+- **Semantic Tokens**: Enhanced syntax highlighting
2525+- **Document Symbols**: Outline view of definitions
2626+- **Workspace Validation**: Multi-file validation with workspace support
2727+2828+## Usage
2929+3030+### Running the Server
3131+3232+```bash
3333+cargo build --release -p mlf-lsp
3434+./target/release/mlf-lsp
3535+```
3636+3737+The server communicates over stdin/stdout using the LSP protocol.
3838+3939+### Editor Integration
4040+4141+#### VS Code
4242+4343+Create a VS Code extension that launches the LSP server:
4444+4545+```json
4646+{
4747+ "languageServer": {
4848+ "module": "/path/to/mlf-lsp",
4949+ "args": [],
5050+ "filetypes": ["mlf"]
5151+ }
5252+}
5353+```
5454+5555+#### Neovim
5656+5757+Configure with `nvim-lspconfig`:
5858+5959+```lua
6060+local lspconfig = require('lspconfig')
6161+local configs = require('lspconfig.configs')
6262+6363+configs.mlf = {
6464+ default_config = {
6565+ cmd = { '/path/to/mlf-lsp' },
6666+ filetypes = { 'mlf' },
6767+ root_dir = lspconfig.util.root_pattern('mlf.toml', '.git'),
6868+ },
6969+}
7070+7171+lspconfig.mlf.setup{}
7272+```
7373+7474+#### Helix
7575+7676+Add to your `languages.toml`:
7777+7878+```toml
7979+[[language]]
8080+name = "mlf"
8181+scope = "source.mlf"
8282+file-types = ["mlf"]
8383+language-servers = ["mlf-lsp"]
8484+8585+[language-server.mlf-lsp]
8686+command = "/path/to/mlf-lsp"
8787+```
8888+8989+## Development
9090+9191+The LSP server is built using:
9292+- **tower-lsp**: High-level LSP framework
9393+- **tokio**: Async runtime
9494+- **mlf-lang**: MLF parser and AST
9595+- **mlf-diagnostics**: Error reporting
9696+9797+### Architecture
9898+9999+```
100100+mlf-lsp/
101101+├── src/
102102+│ ├── lib.rs # Library exports
103103+│ ├── main.rs # Binary entry point
104104+│ └── server.rs # LSP server implementation
105105+└── Cargo.toml
106106+```
107107+108108+### Adding Features
109109+110110+1. **Hover**: Implement position-based type lookup in the AST
111111+2. **Completion**: Build a context-aware symbol table from the workspace
112112+3. **Go to Definition**: Track symbol definitions and references
113113+4. **Formatting**: Generate MLF source from the AST with consistent formatting
114114+115115+## Testing
116116+117117+```bash
118118+# Run tests
119119+cargo test -p mlf-lsp
120120+121121+# Test with LSP inspector
122122+npm install -g @vscode/language-server-inspector
123123+lsp-inspector --command "/path/to/mlf-lsp"
124124+```
125125+126126+## Logging
127127+128128+Set the `RUST_LOG` environment variable to control logging:
129129+130130+```bash
131131+RUST_LOG=debug mlf-lsp
132132+RUST_LOG=mlf_lsp=trace mlf-lsp
133133+```
134134+135135+Logs are written to stderr.
136136+137137+## Contributing
138138+139139+The LSP server is in early development. Contributions are welcome!
140140+141141+Priority features:
142142+1. Hover information with type details
143143+2. Completion for types and keywords
144144+3. Go to definition for imports and references
145145+4. Formatting using mlf-lang AST
+4
mlf-lsp/src/lib.rs
···11+pub mod server;
22+pub mod utils;
33+44+pub use server::MlfLanguageServer;