add hw2
This commit is contained in:
		
							
								
								
									
										419
									
								
								node_modules/langium/lib/grammar/lsp/grammar-code-actions.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										419
									
								
								node_modules/langium/lib/grammar/lsp/grammar-code-actions.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,419 @@ | ||||
| /****************************************************************************** | ||||
|  * Copyright 2021 TypeFox GmbH | ||||
|  * This program and the accompanying materials are made available under the | ||||
|  * terms of the MIT License, which is available in the project root. | ||||
|  ******************************************************************************/ | ||||
| import { CodeActionKind } from 'vscode-languageserver'; | ||||
| import { getContainerOfType } from '../../utils/ast-utils.js'; | ||||
| import { findLeafNodeAtOffset } from '../../utils/cst-utils.js'; | ||||
| import { findNodeForProperty } from '../../utils/grammar-utils.js'; | ||||
| import { escapeRegExp } from '../../utils/regexp-utils.js'; | ||||
| import { UriUtils } from '../../utils/uri-utils.js'; | ||||
| import { DocumentValidator } from '../../validation/document-validator.js'; | ||||
| import * as ast from '../../languages/generated/ast.js'; | ||||
| import { IssueCodes } from '../validation/validator.js'; | ||||
| export class LangiumGrammarCodeActionProvider { | ||||
|     constructor(services) { | ||||
|         this.reflection = services.shared.AstReflection; | ||||
|         this.indexManager = services.shared.workspace.IndexManager; | ||||
|     } | ||||
|     getCodeActions(document, params) { | ||||
|         const result = []; | ||||
|         const acceptor = (ca) => ca && result.push(ca); | ||||
|         for (const diagnostic of params.context.diagnostics) { | ||||
|             this.createCodeActions(diagnostic, document, acceptor); | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
|     createCodeActions(diagnostic, document, accept) { | ||||
|         var _a; | ||||
|         switch ((_a = diagnostic.data) === null || _a === void 0 ? void 0 : _a.code) { | ||||
|             case IssueCodes.GrammarNameUppercase: | ||||
|             case IssueCodes.RuleNameUppercase: | ||||
|                 accept(this.makeUpperCase(diagnostic, document)); | ||||
|                 break; | ||||
|             case IssueCodes.HiddenGrammarTokens: | ||||
|                 accept(this.fixHiddenTerminals(diagnostic, document)); | ||||
|                 break; | ||||
|             case IssueCodes.UseRegexTokens: | ||||
|                 accept(this.fixRegexTokens(diagnostic, document)); | ||||
|                 break; | ||||
|             case IssueCodes.EntryRuleTokenSyntax: | ||||
|                 accept(this.addEntryKeyword(diagnostic, document)); | ||||
|                 break; | ||||
|             case IssueCodes.CrossRefTokenSyntax: | ||||
|                 accept(this.fixCrossRefSyntax(diagnostic, document)); | ||||
|                 break; | ||||
|             case IssueCodes.UnnecessaryFileExtension: | ||||
|                 accept(this.fixUnnecessaryFileExtension(diagnostic, document)); | ||||
|                 break; | ||||
|             case IssueCodes.MissingReturns: | ||||
|                 accept(this.fixMissingReturns(diagnostic, document)); | ||||
|                 break; | ||||
|             case IssueCodes.InvalidInfers: | ||||
|             case IssueCodes.InvalidReturns: | ||||
|                 accept(this.fixInvalidReturnsInfers(diagnostic, document)); | ||||
|                 break; | ||||
|             case IssueCodes.MissingInfer: | ||||
|                 accept(this.fixMissingInfer(diagnostic, document)); | ||||
|                 break; | ||||
|             case IssueCodes.SuperfluousInfer: | ||||
|                 accept(this.fixSuperfluousInfer(diagnostic, document)); | ||||
|                 break; | ||||
|             case DocumentValidator.LinkingError: { | ||||
|                 const data = diagnostic.data; | ||||
|                 if (data && data.containerType === 'RuleCall' && data.property === 'rule') { | ||||
|                     accept(this.addNewRule(diagnostic, data, document)); | ||||
|                 } | ||||
|                 if (data) { | ||||
|                     this.lookInGlobalScope(diagnostic, data, document).forEach(accept); | ||||
|                 } | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         return undefined; | ||||
|     } | ||||
|     /** | ||||
|      * Adds missing returns for parser rule | ||||
|      */ | ||||
|     fixMissingReturns(diagnostic, document) { | ||||
|         const text = document.textDocument.getText(diagnostic.range); | ||||
|         if (text) { | ||||
|             return { | ||||
|                 title: `Add explicit return type for parser rule ${text}`, | ||||
|                 kind: CodeActionKind.QuickFix, | ||||
|                 diagnostics: [diagnostic], | ||||
|                 edit: { | ||||
|                     changes: { | ||||
|                         [document.textDocument.uri]: [{ | ||||
|                                 range: diagnostic.range, | ||||
|                                 newText: `${text} returns ${text}` // suggestion adds missing 'return' | ||||
|                             }] | ||||
|                     } | ||||
|                 } | ||||
|             }; | ||||
|         } | ||||
|         return undefined; | ||||
|     } | ||||
|     fixInvalidReturnsInfers(diagnostic, document) { | ||||
|         const data = diagnostic.data; | ||||
|         if (data && data.actionSegment) { | ||||
|             const text = document.textDocument.getText(data.actionSegment.range); | ||||
|             return { | ||||
|                 title: `Correct ${text} usage`, | ||||
|                 kind: CodeActionKind.QuickFix, | ||||
|                 diagnostics: [diagnostic], | ||||
|                 edit: { | ||||
|                     changes: { | ||||
|                         [document.textDocument.uri]: [{ | ||||
|                                 range: data.actionSegment.range, | ||||
|                                 newText: text === 'infers' ? 'returns' : 'infers' | ||||
|                             }] | ||||
|                     } | ||||
|                 } | ||||
|             }; | ||||
|         } | ||||
|         return undefined; | ||||
|     } | ||||
|     fixMissingInfer(diagnostic, document) { | ||||
|         const data = diagnostic.data; | ||||
|         if (data && data.actionSegment) { | ||||
|             return { | ||||
|                 title: "Correct 'infer' usage", | ||||
|                 kind: CodeActionKind.QuickFix, | ||||
|                 diagnostics: [diagnostic], | ||||
|                 edit: { | ||||
|                     changes: { | ||||
|                         [document.textDocument.uri]: [{ | ||||
|                                 range: { | ||||
|                                     start: data.actionSegment.range.end, | ||||
|                                     end: data.actionSegment.range.end | ||||
|                                 }, | ||||
|                                 newText: 'infer ' | ||||
|                             }] | ||||
|                     } | ||||
|                 } | ||||
|             }; | ||||
|         } | ||||
|         return undefined; | ||||
|     } | ||||
|     fixSuperfluousInfer(diagnostic, document) { | ||||
|         const data = diagnostic.data; | ||||
|         if (data && data.actionRange) { | ||||
|             return { | ||||
|                 title: "Remove the 'infer' keyword", | ||||
|                 kind: CodeActionKind.QuickFix, | ||||
|                 diagnostics: [diagnostic], | ||||
|                 edit: { | ||||
|                     changes: { | ||||
|                         [document.textDocument.uri]: [{ | ||||
|                                 range: data.actionRange, | ||||
|                                 newText: '' | ||||
|                             }] | ||||
|                     } | ||||
|                 } | ||||
|             }; | ||||
|         } | ||||
|         return undefined; | ||||
|     } | ||||
|     fixUnnecessaryFileExtension(diagnostic, document) { | ||||
|         const end = Object.assign({}, diagnostic.range.end); | ||||
|         end.character -= 1; | ||||
|         const start = Object.assign({}, end); | ||||
|         start.character -= '.langium'.length; | ||||
|         return { | ||||
|             title: 'Remove file extension', | ||||
|             kind: CodeActionKind.QuickFix, | ||||
|             diagnostics: [diagnostic], | ||||
|             isPreferred: true, | ||||
|             edit: { | ||||
|                 changes: { | ||||
|                     [document.textDocument.uri]: [{ | ||||
|                             range: { | ||||
|                                 start, | ||||
|                                 end | ||||
|                             }, | ||||
|                             newText: '' | ||||
|                         }] | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
|     makeUpperCase(diagnostic, document) { | ||||
|         const range = { | ||||
|             start: diagnostic.range.start, | ||||
|             end: { | ||||
|                 line: diagnostic.range.start.line, | ||||
|                 character: diagnostic.range.start.character + 1 | ||||
|             } | ||||
|         }; | ||||
|         return { | ||||
|             title: 'First letter to upper case', | ||||
|             kind: CodeActionKind.QuickFix, | ||||
|             diagnostics: [diagnostic], | ||||
|             isPreferred: true, | ||||
|             edit: { | ||||
|                 changes: { | ||||
|                     [document.textDocument.uri]: [{ | ||||
|                             range, | ||||
|                             newText: document.textDocument.getText(range).toUpperCase() | ||||
|                         }] | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
|     addEntryKeyword(diagnostic, document) { | ||||
|         return { | ||||
|             title: 'Add entry keyword', | ||||
|             kind: CodeActionKind.QuickFix, | ||||
|             diagnostics: [diagnostic], | ||||
|             isPreferred: true, | ||||
|             edit: { | ||||
|                 changes: { | ||||
|                     [document.textDocument.uri]: [{ | ||||
|                             range: { start: diagnostic.range.start, end: diagnostic.range.start }, | ||||
|                             newText: 'entry ' | ||||
|                         }] | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
|     fixRegexTokens(diagnostic, document) { | ||||
|         const offset = document.textDocument.offsetAt(diagnostic.range.start); | ||||
|         const rootCst = document.parseResult.value.$cstNode; | ||||
|         if (rootCst) { | ||||
|             const cstNode = findLeafNodeAtOffset(rootCst, offset); | ||||
|             const container = getContainerOfType(cstNode === null || cstNode === void 0 ? void 0 : cstNode.astNode, ast.isCharacterRange); | ||||
|             if (container && container.right && container.$cstNode) { | ||||
|                 const left = container.left.value; | ||||
|                 const right = container.right.value; | ||||
|                 return { | ||||
|                     title: 'Refactor into regular expression', | ||||
|                     kind: CodeActionKind.QuickFix, | ||||
|                     diagnostics: [diagnostic], | ||||
|                     isPreferred: true, | ||||
|                     edit: { | ||||
|                         changes: { | ||||
|                             [document.textDocument.uri]: [{ | ||||
|                                     range: container.$cstNode.range, | ||||
|                                     newText: `/[${escapeRegExp(left)}-${escapeRegExp(right)}]/` | ||||
|                                 }] | ||||
|                         } | ||||
|                     } | ||||
|                 }; | ||||
|             } | ||||
|         } | ||||
|         return undefined; | ||||
|     } | ||||
|     fixCrossRefSyntax(diagnostic, document) { | ||||
|         return { | ||||
|             title: "Replace '|' with ':'", | ||||
|             kind: CodeActionKind.QuickFix, | ||||
|             diagnostics: [diagnostic], | ||||
|             isPreferred: true, | ||||
|             edit: { | ||||
|                 changes: { | ||||
|                     [document.textDocument.uri]: [{ | ||||
|                             range: diagnostic.range, | ||||
|                             newText: ':' | ||||
|                         }] | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
|     fixHiddenTerminals(diagnostic, document) { | ||||
|         const grammar = document.parseResult.value; | ||||
|         const hiddenTokens = grammar.hiddenTokens; | ||||
|         const changes = []; | ||||
|         const hiddenNode = findNodeForProperty(grammar.$cstNode, 'definesHiddenTokens'); | ||||
|         if (hiddenNode) { | ||||
|             const start = hiddenNode.range.start; | ||||
|             const offset = hiddenNode.offset; | ||||
|             const end = grammar.$cstNode.text.indexOf(')', offset) + 1; | ||||
|             changes.push({ | ||||
|                 newText: '', | ||||
|                 range: { | ||||
|                     start, | ||||
|                     end: document.textDocument.positionAt(end) | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|         for (const terminal of hiddenTokens) { | ||||
|             const ref = terminal.ref; | ||||
|             if (ref && ast.isTerminalRule(ref) && !ref.hidden && ref.$cstNode) { | ||||
|                 const start = ref.$cstNode.range.start; | ||||
|                 changes.push({ | ||||
|                     newText: 'hidden ', | ||||
|                     range: { | ||||
|                         start, | ||||
|                         end: start | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|         return { | ||||
|             title: 'Fix hidden terminals', | ||||
|             kind: CodeActionKind.QuickFix, | ||||
|             diagnostics: [diagnostic], | ||||
|             isPreferred: true, | ||||
|             edit: { | ||||
|                 changes: { | ||||
|                     [document.textDocument.uri]: changes | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
|     addNewRule(diagnostic, data, document) { | ||||
|         const offset = document.textDocument.offsetAt(diagnostic.range.start); | ||||
|         const rootCst = document.parseResult.value.$cstNode; | ||||
|         if (rootCst) { | ||||
|             const cstNode = findLeafNodeAtOffset(rootCst, offset); | ||||
|             const container = getContainerOfType(cstNode === null || cstNode === void 0 ? void 0 : cstNode.astNode, ast.isParserRule); | ||||
|             if (container && container.$cstNode) { | ||||
|                 return { | ||||
|                     title: `Add new rule '${data.refText}'`, | ||||
|                     kind: CodeActionKind.QuickFix, | ||||
|                     diagnostics: [diagnostic], | ||||
|                     isPreferred: false, | ||||
|                     edit: { | ||||
|                         changes: { | ||||
|                             [document.textDocument.uri]: [{ | ||||
|                                     range: { | ||||
|                                         start: container.$cstNode.range.end, | ||||
|                                         end: container.$cstNode.range.end | ||||
|                                     }, | ||||
|                                     newText: '\n\n' + data.refText + ':\n    /* TODO implement rule */ {infer ' + data.refText + '};' | ||||
|                                 }] | ||||
|                         } | ||||
|                     } | ||||
|                 }; | ||||
|             } | ||||
|         } | ||||
|         return undefined; | ||||
|     } | ||||
|     lookInGlobalScope(diagnostic, data, document) { | ||||
|         var _a, _b; | ||||
|         const refInfo = { | ||||
|             container: { | ||||
|                 $type: data.containerType | ||||
|             }, | ||||
|             property: data.property, | ||||
|             reference: { | ||||
|                 $refText: data.refText | ||||
|             } | ||||
|         }; | ||||
|         const referenceType = this.reflection.getReferenceType(refInfo); | ||||
|         const candidates = this.indexManager.allElements(referenceType).filter(e => e.name === data.refText); | ||||
|         const result = []; | ||||
|         let shortestPathIndex = -1; | ||||
|         let shortestPathLength = -1; | ||||
|         for (const candidate of candidates) { | ||||
|             if (UriUtils.equals(candidate.documentUri, document.uri)) { | ||||
|                 continue; | ||||
|             } | ||||
|             // Find an import path and a position to insert the import | ||||
|             const importPath = getRelativeImport(document.uri, candidate.documentUri); | ||||
|             let position; | ||||
|             let suffix = ''; | ||||
|             const grammar = document.parseResult.value; | ||||
|             const nextImport = grammar.imports.find(imp => imp.path && importPath < imp.path); | ||||
|             if (nextImport) { | ||||
|                 // Insert the new import alphabetically | ||||
|                 position = (_a = nextImport.$cstNode) === null || _a === void 0 ? void 0 : _a.range.start; | ||||
|             } | ||||
|             else if (grammar.imports.length > 0) { | ||||
|                 // Put the new import after the last import | ||||
|                 const rangeEnd = grammar.imports[grammar.imports.length - 1].$cstNode.range.end; | ||||
|                 if (rangeEnd) { | ||||
|                     position = { line: rangeEnd.line + 1, character: 0 }; | ||||
|                 } | ||||
|             } | ||||
|             else if (grammar.rules.length > 0) { | ||||
|                 // Put the new import before the first rule | ||||
|                 position = (_b = grammar.rules[0].$cstNode) === null || _b === void 0 ? void 0 : _b.range.start; | ||||
|                 suffix = '\n'; | ||||
|             } | ||||
|             if (position) { | ||||
|                 if (shortestPathIndex < 0 || importPath.length < shortestPathLength) { | ||||
|                     shortestPathIndex = result.length; | ||||
|                     shortestPathLength = importPath.length; | ||||
|                 } | ||||
|                 // Add an import declaration for the candidate in the global scope | ||||
|                 result.push({ | ||||
|                     title: `Add import to '${importPath}'`, | ||||
|                     kind: CodeActionKind.QuickFix, | ||||
|                     diagnostics: [diagnostic], | ||||
|                     isPreferred: false, | ||||
|                     edit: { | ||||
|                         changes: { | ||||
|                             [document.textDocument.uri]: [{ | ||||
|                                     range: { | ||||
|                                         start: position, | ||||
|                                         end: position | ||||
|                                     }, | ||||
|                                     newText: `import '${importPath}'\n${suffix}` | ||||
|                                 }] | ||||
|                         } | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         } | ||||
|         // Mark the code action with the shortest import path as preferred | ||||
|         if (shortestPathIndex >= 0) { | ||||
|             result[shortestPathIndex].isPreferred = true; | ||||
|         } | ||||
|         return result; | ||||
|     } | ||||
| } | ||||
| function getRelativeImport(source, target) { | ||||
|     const sourceDir = UriUtils.dirname(source); | ||||
|     let relativePath = UriUtils.relative(sourceDir, target); | ||||
|     if (!relativePath.startsWith('./') && !relativePath.startsWith('../')) { | ||||
|         relativePath = './' + relativePath; | ||||
|     } | ||||
|     if (relativePath.endsWith('.langium')) { | ||||
|         relativePath = relativePath.substring(0, relativePath.length - '.langium'.length); | ||||
|     } | ||||
|     return relativePath; | ||||
| } | ||||
| //# sourceMappingURL=grammar-code-actions.js.map | ||||
		Reference in New Issue
	
	Block a user
	 nik
					nik