Giter VIP home page Giter VIP logo

constrained-editor-plugin's Introduction

Constrained Editor Plugin

A Plugin which adds restrictions to the model of monaco-editor, so that only some parts of the code are editable and rest will become read-only. Please click here for Demo and click here for Documentation

Stats

CodeQL

Table of Contents

Demo

Demo.mov

How to install using NPM

npm i constrained-editor-plugin

Problem Statement

Monaco Editor is one of the most popular code editors in the market. It is developed by Microsoft.The Monaco Editor is the code editor that powers VS Code. Although it is packed with lot of features, it didn't have the feature to constrain the editable area, which is to basically allow editing in only certain parts of the content.

This plugin solves this issue, and will help you add that functionality into your monaco editor instance, without any performance issues.

Sample code

// Include constrainedEditorPlugin.js in your html.
require.config({ paths: { vs: '../node_modules/monaco-editor/dev/vs' } });
require(['vs/editor/editor.main'], function () {
  const container = document.getElementById('container')
  const editorInstance = monaco.editor.create(container, {
    value: [
      'const utils = {};',
      'function addKeysToUtils(){',
      '',
      '}',
      'addKeysToUtils();'
    ].join('\n'),
    language: 'javascript'
  });
  const model = editorInstance.getModel();

  // - Configuration for the Constrained Editor : Starts Here
  const constrainedInstance = constrainedEditor(monaco);
  constrainedInstance.initializeIn(editorInstance);
  constrainedInstance.addRestrictionsTo(model, [{
    // range : [ startLine, startColumn, endLine, endColumn ]
    range: [1, 7, 1, 12], // Range of Util Variable name
    label: 'utilName',
    validate: function (currentlyTypedValue, newRange, info) {
      const noSpaceAndSpecialChars = /^[a-z0-9A-Z]*$/;
      return noSpaceAndSpecialChars.test(currentlyTypedValue);
    }
  }, {
    range: [3, 1, 3, 1], // Range of Function definition
    allowMultiline: true,
    label: 'funcDefinition'
  }]);
  // - Configuration for the Constrained Editor : Ends Here
});

Walkthrough of Sample code

  • constrainedEditor is the globally available class to create an instance of the ConstrainedEditor. This instance has to be created by sending in the monaco variable as an argument.

  • constrainedEditor.initializeIn(editorInstance) is where the constrained editor will add the necessary functions into the editor instance. The Editor returned by the monaco editor during the monaco.editor.create() call should be sent here.

  • constrainedEditor.addRestrictionsTo(model,restrictions) is where the constrained editor will add restrictions to the model.

For detailed documentation on available APIs, click here

Sample Code for monaco-editor react

import { useRef } from "react";
import Editor from "@monaco-editor/react";
import { constrainedEditor } from "constrained-editor-plugin";

function App() {
  const editorRef = useRef(null);
  let restrictions = [];

  function handleEditorDidMount(editor, monaco) {
    editorRef.current = editor;
    const constrainedInstance = constrainedEditor(monaco);
    const model = editor.getModel();
    constrainedInstance.initializeIn(editor);
    restrictions.push({
      range: [1, 1, 2, 10],
      allowMultiline: true
    });
    constrainedInstance.addRestrictionsTo(model, restrictions);
  }

  return (
    <div className="App">
      <Editor
        onMount={handleEditorDidMount}
      />
    </div>
  );
}

export default App;

Thanks @chethan-devopsnow for the sample code : Click here to view discussion about this code

Potential Applications

Coding Tutorial Applications

This plugin can be used in applications which teach programming tutorials, where the application can be made in such as way that it allows users to edit in only certain places

Interviewing applications

This can be used to prevent the candidate to accidentally mess up the boilerplate code given to them.

Contributions and Issues

This project is open source and you are welcome to add more features to this plugin.

If your find any issue, please raise it here

License

Licensed under the MIT License.

constrained-editor-plugin's People

Contributors

dependabot[bot] avatar lsdsjy avatar mloginov avatar niklasmh avatar pranomvignesh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

constrained-editor-plugin's Issues

Change editor value from code while restrictions exist

Is there any clean way to change the editor value from code while restrictions are in place? When I try to do so, it immediately gets reset by the constrained instance, and I cannot edit the value.

I managed to do it by:

  1. Save existing restriction ranges

    const range = constrainedInstance._uriRestrictionMap[model?.uri].getCurrentEditableRanges().state.range;
  2. Remove restrictions

    constrainedInstance.removeRestrictionsIn(model);
  3. Update value

    setEditorValue(newValue); // React value
  4. Reapply restrictions from previously saved value

    constrainedInstance.addRestrictionsTo(model, [
      {
        range: [
          range.startLineNumber,
          range.startColumn,
          range.endLineNumber,
          range.endColumn,
        ],
        label: "state",
        allowMultiline: true,
      },
    ]);

    But I am wondering if there's a better way, as my approach is a little hacky. Thanks!

Adding restrictions after onMount

If you add the restriction interactively (i.e. after onMount), the selected line is not restricted, but everything before and after it is.
`
interface AdminCodeEditorProps {
initialRestrictions: [];
initialCode: string;
}

const AdminCodeEditor: React.FC<AdminCodeEditorProps> = ({
  initialRestrictions,
  initialCode,
}) => {
  const [code, setCode] = useState(
    "const hello = 'world'; \n const hello2 = 'world'; \n "
  );
  const monacoRef = React.useRef<monaco.editor.IStandaloneCodeEditor | null>(
    null
  );
  const editorRef = React.useRef<any>(null);
  const constrainedInstanceRef = React.useRef<any>(null);

  let restrictions = [];

  const handleEditorDidMount: OnMount = (editor, monaco) => {
    monacoRef.current = editor;
    const constrainedInstance = constrainedEditor(monaco);
    const model = editor.getModel();

    constrainedInstance.initializeIn(editor);
    // restrictions.push({
    //   range: [1, 1, 1, 1],
    //   allowMultiline: true,
    // });

    // constrainedInstance.addRestrictionsTo(model, restrictions);
    constrainedInstanceRef.current = constrainedInstance;
  };

  const addRestriction = () => {
    if (monacoRef.current) {
      const model = monacoRef.current.getModel();
      const selection = monacoRef.current.getSelection();

      if (selection) {
        const newRestriction = {
          range: [
            selection.startLineNumber,
            selection.startColumn,
            selection.endLineNumber,
            selection.endColumn,
          ],
          allowMultiline: true,
          label: "test",
        };

        console.warn("newRestriction", newRestriction);

        constrainedInstanceRef.current.addRestrictionsTo(model, [
          newRestriction,
        ]);
      } else {
        console.warn("No selection found");
      }
    }
  };
  return (
    <ContextMenu>
      <ContextMenuTrigger>
        <MonacoEditor
          language="javascript"
          onMount={handleEditorDidMount}
          onChange={(newValue) => setCode(newValue || "")}
          value={code}
          options={{
            minimap: { enabled: false },
            contextmenu: false,
          }}
        />
      </ContextMenuTrigger>
      <ContextMenuContent>
        <ContextMenuItem onClick={addRestriction}>
          Add Restriction
        </ContextMenuItem>
      </ContextMenuContent>
    </ContextMenu>
  );
};

export default AdminCodeEditor;

`

Changing code and restrictions creates bug associated with undo

I'm trying to create constrained editor for multiple files. Restricted editor works fine till I change file and update restrictions. After that, undo event firing, when I'm trying to remove character fires with previously removed characters (for example "'ca" instead of "'", which fired before changing files). This events creates a bug, that slowly shortens restrictions (subtracting from endColumn number of extra characters). You can see this behaviour in attached video.
Link to repo

Screen.Recording.2022-06-02.at.16.45.58.mov

Not all ranges stay editable

Thank you for publishing this plugin!
I just played around a bit in your playground. During that, I noticed that when I do some changes to the second range (// Enter the content for the function here), I am not able to edit the second range (utils) anymore.
Is this some special case and actually meant to be or is it a bug? In the first case, it would be nice to have some information in the documentation.

Tab Character is not respected by constrained editor range type

I am currently working with the Monaco Editor & the Constrained Editor Plugin.

I have defined some code like so, including the use of a tab character:
image

I want to set the editable area to a single line starting from the 5th column onwards:
image

image

Error

image

Does the use of a tab character (when manually setting the value with setValue) not get recognised by the Monaco editor? I was expecting the max column count for line 2 to be 5 after \t and a space

Can I make non editable code hidden?

Hey. Thanks for the plugin!

I have some challenges to solve. Please check code above

function onMessage(msg={name: "", age: 0, arr: [ {k:1}, {k:2}],sub: {test: 32}}): StateObject {
    return {
        CapitalCity: "22",
        Population: msg.arr[0].k,
        ZipCodes:[],
    }
}

All looks great like autocompletion of the msg properties. But I really would like to make it look like below:

function onMessage(msg): StateObject {
    return {
        CapitalCity: "22",
        Population: msg.arr[0].k,
        ZipCodes:[],
    }
}

Any chance to do that with this plugin?
Many thanks in advance!

Is there an implementation available for React?

I am using Monaco editor react to use the monaco editor in my react project. I am intrested into adding freezed ranges(non-editable ranges) in my editor and I have found your plugin, however I am not able to use it in a React component. Is there a way to do that, or maybe there is another similar package?

TypeScript support

First of all, thank you for this great library!

We have an Angular project and we are using the ngx-monaco-editor library from https://github.com/materiahq/ngx-monaco-editor . We managed to make the constrained-editor-plugin work with it but not in a pleasant way. Are you considering adding TypeScript support?

Indicate restricted areas for the end user

It is stated in the README that this plugin can be used in coding tutorial applications. I think that for this use case it is almost necessary to indicate to the user in a nice way, which areas he can actually edit. In issue #4 it got mentioned that the highlighting is a dev feature.
In my opinion, it would be nice if the end user could see the restricted areas in like a pale color or maybe a red. Another option could be, to give the user of the library the possibility, to easily adapt the style of the restricted and unristricted areas. Or is that even easily possible already?
Is a feature like this already planned?

note on how to use with react-monaco-editor

I think it's fairly common to use Monaco in its react form, as I am now. Looking through your documentation, there's no quick-start for applying the plugin to a <MonacoEditor/> JSX element. This would make using it a lot easier in this context.

Change Editable Multi-line Selection Background Color

Hello!

How can I change the background color of editable multi-line text if it is selected? Because currently, it is near impossible to understand if it is selected or not. As an example:

image

I have selected the 4th line as you can see from the light colored dots in this line but it is really hard to understand if it is selected. I want to do something like this:

image

Thanks!

Highlighting the non editable area, and customizing the highlight color

I had a use case where it makes more sense to highlight the non editable zone.
Screenshot 2022-11-19 at 16 24 24

I solved my issue like this:
Screenshot 2022-11-19 at 16 31 58

and I've redefined the css style to make .editableArea--multi-line transparent and .editableArea--single-line to be blue.

But I feel those should be a feature:

  • Allow to highlight the non-editable part instead of the editable.
  • Allow to define a class to a range in order to maximize the customization (maybe under the form .editableArea--myClass)

Anyway thank you for your work.

Doesn't it work with Monaco version 0.33 later?

I'm using the following packages:

vite
vue3
@guolao/vue-monaco-editor:v1.4.1
constrained-editor-plugin:v1.3.0

It works fine with Monaco version 0.33, but starting from version 0.34, I'm encountering an issue where if I input text in a constrained area, the cursor moves to the start of the line (line 1, column 1).

Is there any way to resolve this issue?

Is it possible to hide the constrained part completely?

Use case: providing some extra handmade function for the tutorial. At the same time I also want the auto completion.

Let's say

def uploadToServer(text):
    # some pretty complex function here I'd like to hide

# The visible part
result = computation by the user
uploadToServer(result)
  1. I would use addExtraLib if possible. But it seems it's only available for js/ts and it seems hard to implement for other languages (maybe it depends on the native capability of typescript names are global by default?).
  2. And I also tried provideCompletionItems etc. But it will break the language protocol server linting & completion.
  3. In the end I guess hiding is the best option?

Select All + Delete

It would be nice if there were a way to delete all editable code in the editor. So when you click ctr + a it selects all the code in the editor and if you click delete or backspace it removes all editable code.

Steps

  1. Add an event listener for delete or backspace
  2. Check the selection range on the editor, if all selected
  3. Remove all content in editable space

Release v1.2 to NPM

Good evening,

thank you for this amazing project!

It seems as though version 1.2.0 has not been released to NPM, is that on purpose?

I'm still stuck on the onDidChangeContentFast bug.

Thanks again and best,
Nicolai

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.