diff --git a/.gitignore b/.gitignore index 4ccc1c2..3b35a81 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ dist/* stats.html .vscode node_modules -extensions +/extensions diff --git a/README.md b/README.md index 5d96194..8697438 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ Monaco editor wrapper that adds some features and improvements to it: - It configures the workers - It adds some features: - Smart tabs in Cobol - - `editor.foldAllAutofoldRegions` action - A way to register a text model content provider and a editor open handler - It allows the opening of an overlay editor when navigating to an external file - It adds some language aliases diff --git a/src/editor/autofold.ts b/src/editor/autofold.ts deleted file mode 100644 index 0813f0c..0000000 --- a/src/editor/autofold.ts +++ /dev/null @@ -1,23 +0,0 @@ -import * as monaco from 'monaco-editor' -import { FoldingController, setCollapseStateForMatchingLines } from 'vscode/monaco' - -export default function setup (editor: monaco.editor.IStandaloneCodeEditor): void { - editor.addAction({ - id: 'editor.foldAllAutofoldRegions', - label: 'Fold all autofold regions', - run: async (editor: monaco.editor.ICodeEditor) => { - const foldingController = FoldingController.get(editor) - const foldingModelPromise = foldingController?.getFoldingModel() - if (foldingModelPromise != null) { - return foldingModelPromise.then(foldingModel => { - const editorModel = editor.getModel() - if (editorModel == null || foldingModel == null) { - return - } - const regExp = /.*autofold.*/ - setCollapseStateForMatchingLines(foldingModel, regExp, true) - }) - } - } - }) -} diff --git a/src/editor/cobol.ts b/src/editor/cobol.ts deleted file mode 100644 index 3cacd51..0000000 --- a/src/editor/cobol.ts +++ /dev/null @@ -1,90 +0,0 @@ -import * as monaco from 'monaco-editor' - -const fixedPositions = [0, 6, 7, 11] -const lastFixedPosition = fixedPositions[fixedPositions.length - 1]! - -function getIndentWidth (editor: monaco.editor.IStandaloneCodeEditor): number { - return editor.getModel()!.getOptions().tabSize -} - -function findFirstNonSpacePosition (line: string): number { - for (let i = 0; i < line.length; i++) { - if (line[i] !== ' ') { - return i - } - } - return line.length -} - -function indentLine (editor: monaco.editor.IStandaloneCodeEditor, lineNumber: number) { - if (editor.getModel() == null) { - return - } - const line = editor.getModel()!.getLineContent(lineNumber) - const nonSpacePosition = findFirstNonSpacePosition(line) - const nextFixedPosition = fixedPositions.find(p => p > nonSpacePosition) - - let expectedIndent = nextFixedPosition - if (expectedIndent == null) { - const expectedIndentCount = Math.floor((nonSpacePosition - lastFixedPosition) / getIndentWidth(editor)) + 1 - expectedIndent = lastFixedPosition + getIndentWidth(editor) * expectedIndentCount - } - - const toInsert = expectedIndent - nonSpacePosition - - const range = new monaco.Range( - lineNumber, - 1, - lineNumber, - 1 - ) - - editor.executeEdits('indent', [{ range, text: ' '.repeat(toInsert), forceMoveMarkers: true }]) -} - -function unindentLine (editor: monaco.editor.IStandaloneCodeEditor, lineNumber: number) { - if (editor.getModel() == null) { - return - } - const line = editor.getModel()!.getLineContent(lineNumber) - const nonSpacePosition = findFirstNonSpacePosition(line) - const fixedIndentWidth = getIndentWidth(editor) - const prevFixedPosition = fixedPositions.slice().reverse().find(p => p < nonSpacePosition) ?? 0 - - let expectedIndent = prevFixedPosition - - if (prevFixedPosition === lastFixedPosition) { - // Instead of going back to the last fixed position, go to the nearest (prevFixedPosition + tabSize * N) position - const expectedIndentCount = Math.floor((nonSpacePosition - lastFixedPosition - 1) / fixedIndentWidth) - expectedIndent = lastFixedPosition + fixedIndentWidth * expectedIndentCount - } - - const toRemove = nonSpacePosition - expectedIndent - - const range = new monaco.Range( - lineNumber, - 1, - lineNumber, - 1 + toRemove - ) - - editor.executeEdits('indent', [{ range, text: '', forceMoveMarkers: true }]) -} - -export default function setup (editor: monaco.editor.IStandaloneCodeEditor): void { - editor.addCommand(monaco.KeyCode.Tab, () => { - editor.getSelections()?.forEach(selection => { - for (let lineNumber = selection.startLineNumber; lineNumber <= selection.endLineNumber; lineNumber++) { - indentLine(editor, lineNumber) - } - }) - }, 'editorLangId == cobol && !inSnippetMode') - - editor.addCommand(monaco.KeyMod.Shift | monaco.KeyCode.Tab, () => { - editor.getSelections()?.forEach(selection => { - for (let lineNumber = selection.startLineNumber; lineNumber <= selection.endLineNumber; lineNumber++) { - unindentLine(editor, lineNumber) - } - }) - }, 'editorLangId == cobol && !inSnippetMode') -} diff --git a/src/editor/index.ts b/src/editor/index.ts deleted file mode 100644 index 3ee791a..0000000 --- a/src/editor/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -import * as monaco from 'monaco-editor' -import setupCobol from './cobol' -import setupAutofold from './autofold' - -export default function setup (editor: monaco.editor.IStandaloneCodeEditor): void { - setupCobol(editor) - setupAutofold(editor) -} diff --git a/src/extensions/cobol.ts b/src/extensions/cobol.ts new file mode 100644 index 0000000..74b373f --- /dev/null +++ b/src/extensions/cobol.ts @@ -0,0 +1,101 @@ +import { ExtensionHostKind, registerExtension } from 'vscode/extensions' +import type * as vscode from 'vscode' + +const fixedPositions = [0, 6, 7, 11] +const lastFixedPosition = fixedPositions[fixedPositions.length - 1]! + +function findFirstNonSpacePosition (line: string): number { + for (let i = 0; i < line.length; i++) { + if (line[i] !== ' ') { + return i + } + } + return line.length +} + +const { getApi } = registerExtension({ + name: 'cobol-indent', + publisher: 'codingame', + version: '1.0.0', + engines: { + vscode: '*' + }, + contributes: { + commands: [{ + command: 'cobol-indent', + title: 'Indent cobol', + enablement: 'editorLangId == cobol && !inSnippetMode' + }, { + command: 'cobol-unindent', + title: 'Unindent cobol', + enablement: 'editorLangId == cobol && !inSnippetMode' + }], + keybindings: [{ + command: 'cobol-indent', + key: 'tab', + when: 'editorLangId == cobol && !inSnippetMode' + }, { + command: 'cobol-unindent', + key: 'shift+tab', + when: 'editorLangId == cobol && !inSnippetMode' + }] + } +}, ExtensionHostKind.LocalProcess) + +void getApi().then(api => { + async function indentEditor (editor: vscode.TextEditor) { + await editor.edit(builder => { + for (const selection of editor.selections) { + for (let lineNumber = selection.start.line; lineNumber <= selection.end.line; lineNumber++) { + const line = editor.document.lineAt(lineNumber).text + const nonSpacePosition = findFirstNonSpacePosition(line) + const nextFixedPosition = fixedPositions.find(p => p > nonSpacePosition) + + let expectedIndent = nextFixedPosition + if (expectedIndent == null) { + const indentWidth = editor.options.tabSize as number + const expectedIndentCount = Math.floor((nonSpacePosition - lastFixedPosition) / indentWidth) + 1 + expectedIndent = lastFixedPosition + (indentWidth) * expectedIndentCount + } + + const toInsert = expectedIndent - nonSpacePosition + + builder.insert(new api.Position(lineNumber, 0), ' '.repeat(toInsert)) + } + } + }) + } + + async function unindentEditor (editor: vscode.TextEditor) { + await editor.edit(builder => { + for (const selection of editor.selections) { + for (let lineNumber = selection.start.line; lineNumber <= selection.end.line; lineNumber++) { + const line = editor.document.lineAt(lineNumber).text + const nonSpacePosition = findFirstNonSpacePosition(line) + const fixedIndentWidth = editor.options.tabSize as number + const prevFixedPosition = fixedPositions.slice().reverse().find(p => p < nonSpacePosition) ?? 0 + + let expectedIndent = prevFixedPosition + + if (prevFixedPosition === lastFixedPosition) { + // Instead of going back to the last fixed position, go to the nearest (prevFixedPosition + tabSize * N) position + const expectedIndentCount = Math.floor((nonSpacePosition - lastFixedPosition - 1) / fixedIndentWidth) + expectedIndent = lastFixedPosition + fixedIndentWidth * expectedIndentCount + } + + const toRemove = nonSpacePosition - expectedIndent + + builder.delete(new api.Range( + lineNumber, + 0, + lineNumber, + 0 + toRemove + )) + } + } + }) + } + + api.commands.registerTextEditorCommand('cobol-indent', indentEditor) + api.commands.registerTextEditorCommand('cobol-unindent', unindentEditor) +}) diff --git a/src/extensions/index.ts b/src/extensions/index.ts new file mode 100644 index 0000000..99281a2 --- /dev/null +++ b/src/extensions/index.ts @@ -0,0 +1 @@ +import './cobol' diff --git a/src/monaco.ts b/src/monaco.ts index c6eb842..100b57a 100644 --- a/src/monaco.ts +++ b/src/monaco.ts @@ -2,9 +2,8 @@ import * as monaco from 'monaco-editor' import { IReference, ITextFileEditorModel, createConfiguredEditor, errorHandler, createModelReference as vscodeCreateModelReference } from 'vscode/monaco' import { editorOpenHandlerRegistry, initializePromise, isInitialized } from './services' import './languages' -import './theme' import './worker' -import setupExtensions from './editor' +import './extensions' import { EditorOpenHandler } from './tools/EditorOpenHandlerRegistry' errorHandler.setUnexpectedErrorHandler(error => { @@ -15,11 +14,7 @@ function createEditor (domElement: HTMLElement, options?: monaco.editor.IStandal if (!isInitialized()) { throw new Error('Monaco not initialized') } - const editor = createConfiguredEditor(domElement, options) - - setupExtensions(editor) - - return editor + return createConfiguredEditor(domElement, options) } async function createModelReference (resource: monaco.Uri, content?: string): Promise> { diff --git a/src/theme/index.ts b/src/theme/index.ts deleted file mode 100644 index 76a0458..0000000 --- a/src/theme/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { registerColor } from 'vscode/monaco' - -// Export 3 new colors used -registerColor('statusBar.foreground', { - dark: '#ffffff', - light: '#20252a', - hcDark: '#ffffff', - hcLight: '#20252a' -}, 'Status bar foreground color.') -registerColor('statusBar.background', { - dark: '#252e38', - light: '#ffffff', - hcDark: null, - hcLight: null -}, 'Status bar background color.') -registerColor('statusBar.border', { - dark: '#41454a', - light: '#dadada', - hcDark: '#41454a', - hcLight: '#dadada' -}, 'Status bar border color.')