diff --git a/.editorconfig b/.editorconfig index d115bf2..70db463 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,7 +6,7 @@ trim_trailing_whitespace = true end_of_line = lf insert_final_newline = true -[*.json] +[{*.json,yang.settings}] indent_style = space indent_size = 2 @@ -24,3 +24,7 @@ indent_size = 4 [gradlew.bat] end_of_line = crlf + +[*.yml] +indent_style = space +indent_size = 2 diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..d5def7d --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,27 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Gradle Build", + "type": "shell", + "command": "cd yang-lsp && ./gradlew build", + "problemMatcher": [], + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "Gradle Build Distribution", + "type": "shell", + "command": "cd yang-lsp && ./gradlew --no-daemon build copyDist", + "problemMatcher": [], + "group": { + "kind": "build", + "isDefault": false + } + } + ] +} diff --git a/README.md b/README.md index 13820d3..fd453eb 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # yang-lsp -[![GH Build Status](https://github.com/theia-ide/yang-lsp/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/theia-ide/yang-lsp/actions/workflows/main.yml) + +[![GH Build Status](https://github.com/TypeFox/yang-lsp/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/TypeFox/yang-lsp/actions/workflows/main.yml) [![Build status](https://ci.appveyor.com/api/projects/status/96eo9k5yo0wtpj50/branch/master?svg=true)](https://ci.appveyor.com/project/kittaakos/yang-lsp/branch/master) A language server for YANG (see [Language Server Protocol](https://github.com/Microsoft/language-server-protocol)). @@ -8,34 +9,34 @@ A language server for YANG (see [Language Server Protocol](https://github.com/Mi The language server application is available in two distributions: - - `yang-language-server_.zip` (plain language server) - - `yang-language-server_diagram-extension_.zip` (language server with diagram extension for [sprotty](https://github.com/theia-ide/sprotty)) +- `yang-language-server_.zip` (plain language server) +- `yang-language-server_diagram-extension_.zip` (language server with diagram extension for [sprotty](https://github.com/theia-ide/sprotty)) Both variants include start scripts to launch the background process. Connect its input/output streams to your host application in order to communicate with the language server. The YANG Language Server is currently being used in - - [YANGSTER](https://github.com/theia-ide/yangster) based on [Theia](https://github.com/theia-ide/theia) (incl. diagram extension) - - [Yang VS Code](https://github.com/theia-ide/yang-vscode) available on the [VS Marketplace](https://marketplace.visualstudio.com/items?itemName=typefox.yang-vscode) - - [Yang Eclipse](https://github.com/theia-ide/yang-eclipse) - -## Build - git clone https://github.com/theia-ide/yang-lsp.git \ - && cd yang-lsp/yang-lsp \ - && ./gradlew build +- [YANGSTER](https://github.com/theia-ide/yangster) based on [Theia](https://github.com/theia-ide/theia) (incl. diagram extension) +- [Yang VS Code](https://github.com/TypeFox/yang-vscode) available on the [VS Marketplace](https://marketplace.visualstudio.com/items?itemName=typefox.yang-vscode) +- [Yang Eclipse](https://github.com/theia-ide/yang-eclipse) +## Build -# Release Engineering +```shell + git clone https://github.com/TypeFox/yang-lsp.git + cd yang-lsp/yang-lsp + ./gradlew build +``` -The yang-lsp is the base of multiple binaries +## Release Engineering +The yang-lsp is the base of multiple binaries | Repository | Client | Binary | Bin Repo | CI | Trigger | -| ---------- | ------ | ------ | -------- | --- | ---------- | -| [yang-lsp](https://github.com/theia-ide/yangs-lsp) | LSP | JAR + script | GH Action Artifacts | [GH Action](https://github.com/theia-ide/yang-lsp/actions/workflows/main.yml) | GH Commit / PR | -| [yangster](https://github.com/theia-ide/yangster) | Theia Browser | Docker image | Docker Hub | [Docker Hub](https://hub.docker.com/r/typefox/yangster/builds) | GitHub hook / Jenkins pipeline| -| | Theia | Theia extension| npm | [Jenkins](http://services.typefox.io/open-source/jenkins/job/yangster/) | `yarn run publish` | +| ---------- | ------ | ------ | -------- | --- | ------- | +| [yang-lsp](https://github.com/TypeFox/yangs-lsp) | LSP | JAR + script | GH Action Artifacts | [GH Action](https://github.com/TypeFox/yang-lsp/actions/workflows/main.yml) | GH Commit / PR | +| [yangster](https://github.com/theia-ide/yangster) | Theia Browser | Docker image | Docker Hub | [Docker Hub](https://hub.docker.com/r/typefox/yangster/builds) | GitHub hook / Jenkins pipeline| +| | Theia | Theia extension| npm | [Jenkins](http://services.typefox.io/open-source/jenkins/job/yangster/) | `yarn run publish` | | [yangster-electron](https://github.com/theia-ide/yangster-electron) | Theia Electron | executables | ? | ? | ? | | [yang-eclipse](https://github.com/theia-ide/yang-eclipse) | Eclipse | p2 update site | Eclipse Marketplace | [Jenkins](http://services.typefox.io/open-source/jenkins/job/yang-eclipse/) | GitHub hook / Jenkins pipeline | -| [yang-vscode](https://github.com/theia-ide/yang-vscode) | VSCode | VSCode extension | VSCode Marketplace | - | `vsce` | - +| [yang-vscode](https://github.com/TypeFox/yang-vscode) | VSCode | VSCode extension | VSCode Marketplace | - | `vsce` | diff --git a/appveyor.yml b/appveyor.yml index 4266a7f..ba13b31 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,5 +11,5 @@ build_script: - ./yang-lsp/gradlew.bat -p yang-lsp build --refresh-dependencies --no-daemon cache: - - C:\Users\appveyor\.gradle - - C:\Users\appveyor\.m2 \ No newline at end of file + - C:\Users\appveyor\.gradle + - C:\Users\appveyor\.m2 diff --git a/docs/Extensions.md b/docs/Extensions.md index 714241f..7c566bb 100644 --- a/docs/Extensions.md +++ b/docs/Extensions.md @@ -1,15 +1,16 @@ # Extensions -The yang-lsp allows to have additional third party extensions, configured through the [`yang.settings` file](Settings.md). -That file must be located in the root of an opened directory must conform to JSON syntax. +The yang-lsp allows to have additional third party extensions, configured through `yang.settings`. +For details on file format and schema see [Settings.md](./Settings.md). So far two different kinds of extensions are supported : - - Validators (`IValidatorExtension`) - - Commands (`ICommandExtension`) + +- Validators (`IValidatorExtension`) +- Commands (`ICommandExtension`) ## Create a Validator -A validator extension is a Java class that implements the interface `io.typefox.yang.validation.IValidatorExtension`. +A validator extension is a Java class that implements the interface `io.typefox.yang.validation.IValidatorExtension`. Here is a small example: ```java @@ -19,72 +20,77 @@ package my.pack; public class MyExampleValidator implements IValidatorExtension { - public static final String BAD_NAME = "bad_name"; + public static final String BAD_NAME = "bad_name"; - @Override - public void validate(AbstractModule module, IAcceptor issueAcceptor, CancelIndicator cancelIndicator) { - if (module.getName().equals("foo")) { - issueAcceptor.accept(IssueFactory.createIssue(module, YangPackage.Literals.ABSTRACT_MODULE__NAME, "'foo' is a bad name", BAD_NAME)); - } - } + @Override + public void validate(AbstractModule module, IAcceptor issueAcceptor, CancelIndicator cancelIndicator) { + if (module.getName().equals("foo")) { + issue = IssueFactory.createIssue(module, YangPackage.Literals.ABSTRACT_MODULE__NAME, + "'foo' is a bad name", BAD_NAME) + issueAcceptor.accept(); + } + } } -``` +``` ## Create a Command Extension -A command extension contributes actions, that will be shown in the context menu of a supporting client (currently only Yangster supports it). +A command extension contributes actions, that will be shown in the context menu +of a supporting client (currently only Yangster supports it). + Here is an example: -```xtend +```java class MyCommand implements ICommandExtension { - static val COMMAND = "Create a file name 'foo.txt'." - - /** - * return a list of commands. A command string is used as ID internally and as a label in the UI. - */ - override getCommands() { - #[COMMAND] - } - - /** - * Called when the user asked to execute a certain command. - */ - override executeCommand(String command, Resource resource, LanguageClient client) { - if (COMMAND == command) { - // get the project directory - val uri = ProjectConfigAdapter.findInEmfObject(resource.resourceSet)?.projectConfig?.path.toFileString - val f = new File(uri, 'foo.txt') - if (f.exists) { - client.showMessage(new MessageParams => [ - message = 'Such a file already exists.' - ]) - } else { - f.createNewFile - } - } - } - + static val COMMAND = "Create a file name 'foo.txt'." + + /** + * return a list of commands. A command string is used as ID internally and as a label in the UI. + */ + override getCommands() { + #[COMMAND] + } + + /** + * Called when the user asked to execute a certain command. + */ + override executeCommand(String command, Resource resource, LanguageClient client) { + if (COMMAND == command) { + // get the project directory + val uri = ProjectConfigAdapter.findInEmfObject(resource.resourceSet)?.projectConfig?.path.toFileString + val f = new File(uri, 'foo.txt') + if (f.exists) { + client.showMessage(new MessageParams => [ + message = 'Such a file already exists.' + ]) + } else { + f.createNewFile + } + } + } + } ``` - ## Package an extension -The class needs to be packaged in a jar. So simply use the java build tool of your preference to create a jar from it. Put the jar somewhere relative to the project's root directory. +The class needs to be packaged in a jar. So simply use the java build tool of +your preference to create a jar from it. Put the jar somewhere relative to the +project's root directory. ## Add the plugin -Create (or open) the `yang.settings` file and add the plugin using the following configuration: +Create (or open) the `yang.settings` file and add the plugin using the following +configuration: ```json - "extension" : { - "classpath" : "extension.jar", - "validators" : "my.pack.MyExampleValidator", - "commands" : "my.pack.MyCommand" - } + "extension" : { + "classpath" : "extension.jar", + "validators" : "my.pack.MyExampleValidator", + "commands" : "my.pack.MyCommand" + } ``` The language server will automatically pick up the extension. - diff --git a/docs/Processing_Files.md b/docs/Processing_Files.md index 80673c5..ca85150 100644 --- a/docs/Processing_Files.md +++ b/docs/Processing_Files.md @@ -1,10 +1,10 @@ -# Processing YANG files +# Processing YANG files As yang-lsp contains all the tools to parse, link and validate YANG models. If you want to further process the YANG files you authored with yang-lsp it makes sense to make reuse of the existing functionality. Here is some example code in Xtend for an application that reads in all YANG files from a given directory. The files are parsed into our YANG EMF model, all cross-references are resolved and all files are validated. If there are no errors, the method `generate()` is called for all resources: -```Xtend +```xtend package io.typefox.yang.example import com.google.inject.Inject @@ -20,66 +20,67 @@ import org.eclipse.xtext.util.CancelIndicator import org.eclipse.xtext.validation.CheckMode import org.eclipse.xtext.validation.IResourceValidator -class StandaloneExample { - - def static void main(String... args) { - val injector = new YangStandaloneSetup().createInjectorAndDoEMFRegistration - injector.getInstance(StandaloneExample).run(args) - } - - @Inject XtextResourceSet resourceSet - @Inject IResourceValidator validator - boolean hasErrors = false - - def void run(String... args) { - addFile(new File(args.head)) - resourceSet.resources.forEach [ resource | - EcoreUtil.resolveAll(resource) - val issues = validator.validate(resource, CheckMode.ALL, CancelIndicator.NullImpl) - issues.forEach [ issue | - hasErrors = hasErrors || issue.severity === Severity.ERROR - System.err.println(issue) - ] - ] - if(!hasErrors) { - resourceSet.resources.forEach [ - generate - ] - } - } - - def void addFile(File file) { - if(file.isDirectory) - file.listFiles.forEach[ addFile ] - else if(file.name.endsWith('.yang')) - resourceSet.getResource(URI.createURI(file.toURI.toString), true) - } - - def generate(Resource resource) { - // do your own processing here - resource.allContents.filter(Module).forEach [ module | - println(''' - Found module «module.name» - ''') - ] - } +class StandaloneExample { + + def static void main(String... args) { + val injector = new YangStandaloneSetup().createInjectorAndDoEMFRegistration + injector.getInstance(StandaloneExample).run(args) + } + + @Inject XtextResourceSet resourceSet + @Inject IResourceValidator validator + boolean hasErrors = false + + def void run(String... args) { + addFile(new File(args.head)) + resourceSet.resources.forEach [ resource | + EcoreUtil.resolveAll(resource) + val issues = validator.validate(resource, CheckMode.ALL, CancelIndicator.NullImpl) + issues.forEach [ issue | + hasErrors = hasErrors || issue.severity === Severity.ERROR + System.err.println(issue) + ] + ] + if(!hasErrors) { + resourceSet.resources.forEach [ + generate + ] + } + } + + def void addFile(File file) { + if(file.isDirectory) + file.listFiles.forEach[ addFile ] + else if(file.name.endsWith('.yang')) + resourceSet.getResource(URI.createURI(file.toURI.toString), true) + } + + def generate(Resource resource) { + // do your own processing here + resource.allContents.filter(Module).forEach [ module | + println(''' + Found module «module.name» + ''') + ] + } } ``` -A number of useful helper methods can be found in the [utils package](https://github.com/theia-ide/yang-lsp/tree/master/yang-lsp/io.typefox.yang/src/main/java/io/typefox/yang/utils) + +A number of useful helper methods can be found in the [utils package](https://github.com/TypeFox/yang-lsp/tree/master/yang-lsp/io.typefox.yang/src/main/java/io/typefox/yang/utils) Here is sample `build.gradle` to build the above class: ```groovy buildscript { - repositories.jcenter() - dependencies { - classpath 'org.xtext:xtext-gradle-plugin:1.0.19' - } + repositories.jcenter() + dependencies { + classpath 'org.xtext:xtext-gradle-plugin:1.0.19' + } } repositories { jcenter() - maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } + maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } } apply plugin: 'org.xtext.xtend' @@ -90,7 +91,7 @@ apply plugin: 'maven' group = 'io.typefox.yang' version = '0.1.0-SNAPSHOT' -dependencies { +dependencies { compile 'io.typefox.yang:io.typefox.yang:0.1.0-SNAPSHOT' compile 'org.eclipse.xtext:org.eclipse.xtext:2.13.0' compile 'org.eclipse.xtend:org.eclipse.xtend.lib:2.13.0' diff --git a/docs/Settings.md b/docs/Settings.md index 3e5dbf9..b32d08a 100644 --- a/docs/Settings.md +++ b/docs/Settings.md @@ -1,11 +1,13 @@ # Settings -The yang-lsp allos the user to configure various settings, though setting files. -A setting file has the name `yang.settings` and can be located - - at the root of a project - - in the user's directory under `~/.yang/yang.settings`. +The yang-lsp allows users to configure various settings, through settings files. +The settings file has the name `yang.settings` and can be located -The file syntax is a JSONC and its schema can be found [here](../schema/yang-lsp-settings-schema.json). +- at the root of a project +- in the user's directory under `~/.yang/yang.settings`. + +The file syntax is a [JSON with Comments][1], and its [JSON Schema][2] can be +found [here](../schema/yang-lsp-settings-schema.json). ## Disable Code Lens @@ -48,12 +50,14 @@ The settings is used to register an extension. Please find the details [here](Ex ## Diagnostics The user can change the severity of diagnostics, by setting the value of a diagnostic preference key to either - - `"error"` - - `"warning"` - - `"ignore"` + +- `"error"` +- `"warning"` +- `"ignore"` The settings contains a `diagnostics` section in which the serverioties for the below diagnostics can be adjusted. An example : + ```json { "diagnostic" : { @@ -62,6 +66,8 @@ An example : } ``` +### Diagnostic Codes + #### `substatement-cardinality` Issue code that are entangled with cardinality problems of container statement's sub-statements. @@ -82,7 +88,7 @@ Issue code for cases when a sub-statement incorrectly precedes another sub-state #### `incorrect-version` -Issues code that is used when a module has anything but {@code '1.1'} version. +Issue code that is used when a module has anything but {@code '1.1'} version. (default severity: error) @@ -100,7 +106,6 @@ A duplicate local name. #### `missing-prefix` - (default severity: error) #### `missing-revision` @@ -148,13 +153,13 @@ For instance; the name contains any invalid characters, or equals to any YANG bu #### `bad-include-yang-version` -Issues code when there is an inconsistency between a module's version and the version of the included modules. +Issue code when there is an inconsistency between a module's version and the version of the included modules. (fixed severity: error) #### `bad-import-yang-version` -Issues code when there is an inconsistency between a module's version and the version of the included modules. +Issue code when there is an inconsistency between a module's version and the version of the included modules. (fixed severity: error) @@ -184,7 +189,7 @@ Issue code indicating that an enumerable introduces a new value that is not decl #### `key-duplicate-leaf-name` -Issues code for indicating a duplicate leaf node name in a key. +Issue code for indicating a duplicate leaf node name in a key. (default severity: error) @@ -202,7 +207,7 @@ Controls the indentation string when formatting or serializing yang files. #### `invalid-config` -Issue code when a `config=true` is a child of a `config=false` (see https://tools.ietf.org/html/rfc7950#section-7.21.1) +Issue code when a `config=true` is a child of a `config=false` (see ) (default severity: error) @@ -273,3 +278,5 @@ Diagnostic for unresolvable Xpath expressions. (default severity: ignore) +[1]: https://code.visualstudio.com/docs/languages/json#_json-with-comments +[2]: https://json-schema.org/ diff --git a/schema/yang-lsp-settings-schema.json b/schema/yang-lsp-settings-schema.json index 78475f0..45b71fd 100644 --- a/schema/yang-lsp-settings-schema.json +++ b/schema/yang-lsp-settings-schema.json @@ -2,7 +2,8 @@ "$id": "https://github.com/TypeFox/yang-lsp/blob/master/schema/yang-lsp-settings-schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "title": "YANG LSP Settings JSON Schema", - "description": "JSON Schema for YANG LSP settings file 'yang.settings'.\nSee https://github.com/TypeFox/yang-lsp/blob/master/docs/Settings.md", + "description": "JSON Schema for YANG LSP settings file 'yang.settings'", + "markdownDescription": "See [documentation](https://github.com/TypeFox/yang-lsp/blob/master/docs/Settings.md)", "type": "object", "properties": { "code-lens-enabled": { @@ -32,13 +33,26 @@ }, "validators": { "description": "IValidatorExtension implementor class e.g., 'my.pack.MyExampleValidator'", + "markdownDescription": "`IValidatorExtension` implementor class e.g., `my.pack.MyExampleValidator`", "type": "string" }, "commands": { - "description": "ICommandExtension implementor class e.g., 'my.pack.MyCommand'", + "description": "ICommandExtension implementor class e.g., 'my.pack.MyExampleCommand'", + "markdownDescription": "`ICommandExtension` implementor class e.g., `my.pack.MyExampleCommand`", "type": "string" } }, + "defaultSnippets": [ + { + "label": "New YANG LSP extension", + "description": "Hooks YANG LSP extension to the language server instance", + "body": { + "classpath": "${1:project/relative/path/to/extension.jar}", + "validators": "${2:my.pack.MyExampleValidator}", + "commands": "${3:my.pack.MyExampleCommand}" + } + } + ], "required": [ "classpath" ], @@ -78,7 +92,7 @@ "default": "error" }, "incorrect-version": { - "description": "Issues code that is used when a module has anything but '1.1' version.", + "description": "Issue code that is used when a module has anything but '1.1' version.", "$ref": "#/properties/diagnostic/$defs/severity", "default": "error" }, @@ -128,12 +142,12 @@ "default": "error" }, "bad-include-yang-version": { - "description": "Issues code when there is an inconsistency between a module's yang-version and the yang-version of the included modules.", + "description": "Issue code when there is an inconsistency between a module's yang-version and the yang-version of the included modules.", "$ref": "#/properties/diagnostic/$defs/fixed-severity", "default": "error" }, "bad-import-yang-version": { - "description": "Issues code when there is an inconsistency between a module's yang-version and the yang-version of the imported modules.", + "description": "Issue code when there is an inconsistency between a module's yang-version and the yang-version of the imported modules.", "$ref": "#/properties/diagnostic/$defs/fixed-severity", "default": "error" }, @@ -158,7 +172,7 @@ "default": "error" }, "key-duplicate-leaf-name": { - "description": "Issues code for indicating a duplicate leaf node name in a key.", + "description": "Issue code for indicating a duplicate leaf node name in a key.", "$ref": "#/properties/diagnostic/$defs/severity", "default": "error" }, @@ -174,7 +188,8 @@ "default": " " }, "invalid-config": { - "description": "Issue code when a config=true is a child of a config=false (see https://tools.ietf.org/html/rfc7950#section-7.21.1)", + "description": "Issue code when a config=true is a child of a config=false", + "markdownDescription": "Issue code when a `config true` node is a child of a `config false` node.\n\nSee [IETF RFC 7950 Section 7.21.1](https://tools.ietf.org/html/rfc7950#section-7.21.1)", "$ref": "#/properties/diagnostic/$defs/severity", "default": "error" }, diff --git a/yang-lsp/io.typefox.yang/yang.settings b/yang-lsp/io.typefox.yang/yang.settings index 59642bf..8fbd50f 100644 --- a/yang-lsp/io.typefox.yang/yang.settings +++ b/yang-lsp/io.typefox.yang/yang.settings @@ -1,3 +1,3 @@ { - "excludePath": "build:bin" -} \ No newline at end of file + "excludePath": "build:bin" +} diff --git a/yang-lsp/release.md b/yang-lsp/release.md index c73504a..61b99d0 100644 --- a/yang-lsp/release.md +++ b/yang-lsp/release.md @@ -1,4 +1,4 @@ -### Steps to follow +# Steps to follow - Create local branch e.g. `release-0.5.0` - Update the version to `version = '0.5.0'` in `yang-lsp/gradle/versions.gradle` and commit @@ -10,4 +10,4 @@ - Go to [OSS Staging Repository](https://oss.sonatype.org/#stagingRepositories) and __Close__ -> __Release__ the staging yang repositories - Check the released version inside the [maven repo](https://repo1.maven.org/maven2/io/typefox/yang/) - Check if dependent projects (yang-vscode) need to be updated -- Switch to master. Change gradle version to next snapshot e.g. 0.5.1-SNAPSHOT and commit. \ No newline at end of file +- Switch to master. Change gradle version to next snapshot e.g. 0.5.1-SNAPSHOT and commit.