extension.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import * as vscode from 'vscode';
  2. import { createDiagnostics } from './diagnostics';
  3. import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient/node';
  4. const KEYWORDS = new Set([
  5. 'AND', 'ARRAY', 'BEGIN', 'CASE', 'CONST', 'DIV', 'DO', 'DOWNTO',
  6. 'ELSE', 'END', 'FILE', 'FOR', 'FUNCTION', 'GOTO', 'IF', 'IN',
  7. 'LABEL', 'MOD', 'NIL', 'NOT', 'OF', 'OR', 'PACKED', 'PROCEDURE',
  8. 'PROGRAM', 'RECORD', 'REPEAT', 'SET', 'THEN', 'TO', 'TYPE', 'UNIT',
  9. 'UNTIL', 'USES', 'VAR', 'WHILE', 'WITH', 'FORWARD'
  10. ]);
  11. let applyingEdit = false;
  12. function isIdentifierCharacter(ch: string | undefined): boolean {
  13. return !!ch && /[A-Za-z0-9_]/.test(ch);
  14. }
  15. export function activate(context: vscode.ExtensionContext): void {
  16. createDiagnostics(context);
  17. const serverModule = context.asAbsolutePath('out/server.js');
  18. const serverOptions: ServerOptions = {
  19. run: { module: serverModule, transport: TransportKind.ipc },
  20. debug: { module: serverModule, transport: TransportKind.ipc }
  21. };
  22. const cfg = vscode.workspace.getConfiguration('modula2');
  23. const clientOptions: LanguageClientOptions = {
  24. documentSelector: [
  25. { scheme: 'file', language: 'pascal' }
  26. ],
  27. synchronize: { configurationSection: 'modula2' },
  28. initializationOptions: {
  29. pascalDialect: cfg.get<string>('pascal.dialect', 'freepascal'),
  30. pascalValidators: cfg.get<object>('pascal.validators', {}),
  31. format: {
  32. indentSize: cfg.get<number>('pascal.format.indentSize', 2),
  33. useTabs: cfg.get<boolean>('pascal.format.useTabs', false),
  34. keywordCase: cfg.get<string>('pascal.format.keywordCase', 'upper'),
  35. spaceAroundOperators: cfg.get<boolean>('pascal.format.spaceAroundOperators', true),
  36. spaceAfterComma: cfg.get<boolean>('pascal.format.spaceAfterComma', true),
  37. spaceBeforeColon: cfg.get<boolean>('pascal.format.spaceBeforeColon', false),
  38. spaceAroundRange: cfg.get<boolean>('pascal.format.spaceAroundRange', true),
  39. emptyLineLimit: cfg.get<number>('pascal.format.emptyLineLimit', 1),
  40. trimTrailingWhitespace: cfg.get<boolean>('pascal.format.trimTrailingWhitespace', true),
  41. insertFinalNewline: cfg.get<boolean>('pascal.format.insertFinalNewline', true)
  42. }
  43. }
  44. };
  45. const client = new LanguageClient('pascalCocoR', 'Pascal Coco/R Language Server', serverOptions, clientOptions);
  46. context.subscriptions.push(client);
  47. void client.start();
  48. context.subscriptions.push(vscode.workspace.onDidChangeTextDocument(async event => {
  49. if (applyingEdit || event.document.languageId !== 'pascal' ||
  50. !vscode.workspace.getConfiguration('modula2').get<boolean>('pascal.uppercaseKeywords', true)) {
  51. return;
  52. }
  53. const editor = vscode.window.activeTextEditor;
  54. if (!editor || editor.document.uri.toString() !== event.document.uri.toString()) {
  55. return;
  56. }
  57. for (const change of event.contentChanges) {
  58. const position = change.range.start.translate(0, change.text.length);
  59. const line = event.document.lineAt(position.line).text;
  60. let start = position.character;
  61. while (start > 0 && isIdentifierCharacter(line[start - 1])) {
  62. start--;
  63. }
  64. let end = position.character;
  65. while (end < line.length && isIdentifierCharacter(line[end])) {
  66. end++;
  67. }
  68. // Do not modify a token while it is still being typed.
  69. // This is what prevents "beginning" from becoming "BEGINning".
  70. if (end >= line.length || isIdentifierCharacter(line[end])) {
  71. continue;
  72. }
  73. const token = line.slice(start, end);
  74. const upper = token.toUpperCase();
  75. if (!KEYWORDS.has(upper) || token === upper) {
  76. continue;
  77. }
  78. applyingEdit = true;
  79. try {
  80. await editor.edit(builder => {
  81. builder.replace(
  82. new vscode.Range(position.line, start, position.line, end),
  83. upper
  84. );
  85. });
  86. } finally {
  87. applyingEdit = false;
  88. }
  89. }
  90. }));
  91. }
  92. export function deactivate(): void {}