bouzidanas / streamlit-code-editor

A code editor component for Streamlit apps, built on top of react-ace, with custom themes and customizable interface elements.
MIT License
98 stars 11 forks source link

How can I add my own autocompletion words? #17

Open itsToggle opened 4 months ago

itsToggle commented 4 months ago

Hey, thanks so much for this project, its been a wonderful addition to my projects :) I noticed that the section on autocompletion/snippets is not finished in the docs, could you give any hints as to how I could implement basic autocompletion for a list of words? Thanks!

bouzidanas commented 4 months ago

The feature to add/remove autocompletion tokens is not finished. You can only turn it on off.

However, there is a way to add/remove snippets.

response = code_editor(code_text, snippets=["",""])

The above example shows the default value for the snippets argument of code_editor. I show this to you to explain that the first element of the array is where you will place the snippets you want to add and the second element is where you want to place the array of snippets you want to remove.

Example: Add markdown snippets while removing none of the pre-existing snippets.

reveal_snippets = [[{ 
                        "name": '--', 
                        "code": '\n---\n## New Horizontal Slide\n' 
                    },
                    { 
                        "name": 'new slide (horizontal)', 
                        "code": '\n---\n## New Horizontal Slide\n' 
                    },
                    { 
                        "name": '-', 
                        "code": '\n--\n## New Vertical Slide\n' 
                    },
                    { 
                        "name": 'new slide (vertical)', 
                        "code": '--\n## New Vertical Slide\n' 
                    }, 
                    { 
                        "name": '<!-- .s', 
                        "code": '<!-- .slide:  -->'
                    },
                    { 
                        "name": '<!-- .e', 
                        "code": '<!-- .element: class="fragment" class="fragment" -->'
                    },
                    {
                        "name": 'slide attributes', 
                        "code": '<!-- .slide:  -->'
                    }, 
                    { 
                        "name": 'element attributes', 
                        "code": '<!-- .element: class="fragment" class="fragment" -->'
                    } 
                  ],""]    # <-- notice the empty string. This tells code editor to not remove any snippets. This is also necessary to ensure the expected format

response = code_editor(code_text, lang="markdown", snippets=reveal_snippets)

Hopefully the example is helpful.

I will try to work on adding a new argument that allows you to add/remove autocomplete tokens/words

bouzidanas commented 4 months ago

I forgot to mention. I think you can provide the snippets you want to add or remove in either an array like in the example above or as a string.

As an example, I will list the default snippets for python (commented out):

#snippet #!
#   #!/usr/bin/env python
#snippet imp
#   import ${1:module}
#snippet from
#   from ${1:package} import ${2:module}
#snippet docs
#   '''
#   File: ${1:FILENAME:file_name}
#   Author: ${2:author}
#   Description: ${3}
#   '''
#snippet wh
#   while ${1:condition}:
#       ${2:# TODO: write code...}
#snippet dowh
#   while True:
#       ${1:# TODO: write code...}
#       if ${2:condition}:
#           break
#snippet with
#   with ${1:expr} as ${2:var}:
#       ${3:# TODO: write code...}
#snippet cl
#   class ${1:ClassName}(${2:object}):
#       """${3:docstring for $1}"""
#       def __init__(self, ${4:arg}):
#           ${5:super($1, self).__init__()}
#           self.$4 = $4
#           ${6}
#snippet def
#   def ${1:fname}(${2:`indent('.') ? 'self' : ''`}):
#       """${3:docstring for $1}"""
#       ${4:# TODO: write code...}
#snippet deff
#   def ${1:fname}(${2:`indent('.') ? 'self' : ''`}):
#       ${3:# TODO: write code...}
#snippet defs
#   def ${1:mname}(self, ${2:arg}):
#       ${3:# TODO: write code...}
#snippet property
#   def ${1:foo}():
#       doc = "${2:The $1 property.}"
#       def fget(self):
#           ${3:return self._$1}
#       def fset(self, value):
#           ${4:self._$1 = value}
#snippet if
#   if ${1:condition}:
#       ${2:# TODO: write code...}
#snippet el
#   else:
#       ${1:# TODO: write code...}
#snippet ei
#   elif ${1:condition}:
#       ${2:# TODO: write code...}
#snippet for
#   for ${1:item} in ${2:items}:
#       ${3:# TODO: write code...}
#snippet cutf8
#   # -*- coding: utf-8 -*-
#snippet clatin1
#   # -*- coding: latin-1 -*-
#snippet cascii
#   # -*- coding: ascii -*-
#snippet ld
#   ${1:var} = lambda ${2:vars} : ${3:action}
#snippet .
#   self.
#snippet try Try/Except
#   try:
#       ${1:# TODO: write code...}
#   except ${2:Exception}, ${3:e}:
#       ${4:raise $3}
#snippet try Try/Except/Else
#   try:
#       ${1:# TODO: write code...}
#   except ${2:Exception}, ${3:e}:
#       ${4:raise $3}
#   else:
#       ${5:# TODO: write code...}
#snippet try Try/Except/Finally
#   try:
#       ${1:# TODO: write code...}
#   except ${2:Exception}, ${3:e}:
#       ${4:raise $3}
#   finally:
#       ${5:# TODO: write code...}
#snippet try Try/Except/Else/Finally
#   try:
#       ${1:# TODO: write code...}
#   except ${2:Exception}, ${3:e}:
#       ${4:raise $3}
#   else:
#       ${5:# TODO: write code...}
#   finally:
#       ${6:# TODO: write code...}
#snippet ifmain
#   if __name__ == '__main__':
#       ${1:main()}
#snippet _
#   __${1:init}__${2}
#snippet pdb
#   import pdb; pdb.set_trace()
#snippet ipdb
#   import ipdb; ipdb.set_trace()
#snippet pdbbb
#   import pdbpp; pdbpp.set_trace()
#snippet pprint
#   import pprint; pprint.pprint(${1})${2}
#snippet "
#   """
#   ${1:doc}
#   """
#snippet test
#   def test_${1:description}(${2:self}):
#       ${3:# TODO: write code...}
#snippet testcase
#   class ${1:ExampleCase}(unittest.TestCase):
#       
#       def test_${2:description}(self):
#           ${3:# TODO: write code...}
#snippet fut
#   from __future__ import ${1}
#snippet getopt
#   try:
#       # Short option syntax: "hv:"
#       # Long option syntax: "help" or "verbose="
#       opts, args = getopt.getopt(sys.argv[1:], "${1:short_options}", [${2:long_options}])
#   
#   except getopt.GetoptError, err:
#       # Print debug info
#       print str(err)
#       ${3:error_action}
#   
#   for option, argument in opts:
#       if option in ("-h", "--help"):
#           ${4}
#       elif option in ("-v", "--verbose"):
#           verbose = argument

For a guide to writing snippets see here

itsToggle commented 4 months ago

Awesome! Thanks 👍 For my purposes this would close the issue.

ewoo17 commented 3 months ago

Hey @bouzidanas - Thanks for this wonderful component. I've been trying to figure out how to use the new completions and replace_completer params, but can't seem to get it working. Are you still planning to update the docs to address those latest changes? Thanks!

bouzidanas commented 3 months ago

Hi @ewoo17 ,

I got pulled away from that to fix some issues in another package. I will add some info that should help soon.

bouzidanas commented 3 months ago

Hi again @ewoo17

New section in the docs covering completions:

https://code-editor-documentation.streamlit.app/Advanced_customization#custom-completions

ewoo17 commented 3 months ago

@bouzidanas I've copied your example in the docs into a very basic streamlit app, however I don't see the completion. Any ideas?

version:

% pip show streamlit_code_editor  
Name: streamlit-code-editor
Version: 0.1.20
Summary: React-ace editor customized for Streamlit
Home-page: https://github.com/bouzidanas/streamlit-code-editor
Author: Anas Bouzid
Author-email: anasbouzid@gmail.com
License: 
Location: <path_to_working_dir>/venv/lib/python3.12/site-packages
Requires: streamlit
Required-by: 

app.py, running streamlit run app.py:

import streamlit as st

from code_editor import code_editor

response_dict = code_editor(
    "a bunch of random stuff",
    lang="python",
    completions=[{"caption": "AAA", "value": "BBB", "meta": "CCC", "name": "DDD", "score": 400}],
    height=[25, 25],
)

Screenshot:

Screenshot 2024-06-20 at 10 15 30 AM
bouzidanas commented 3 months ago

Found out what the issue was. The latest package version number was incorrect. The 2 in 0.1.20 was for some reason changed to a 1.

That is now corrected and a new version of the package was uploaded. Please update the package by running

pip install -U streamlit-code-editor
ewoo17 commented 3 months ago

Awesome, that worked. Thanks for the quick help!

bouzidanas commented 3 months ago

Thank you for making me aware of the issue!

ewoo17 commented 3 months ago

So looks like with replace_completer=True you pop() the keyWordCompleter of the specific language mode off, however, the textCompleter (local words/variables) remains - wondering if that's by intention or what your thoughts were when planning it out?

fyi: For my use case/language, I'd rather not see those suggestions for every word that ends up being typed in the editor, as they end up not being useful/anything the user would want to potentially type.

perhaps instead of replace_completer: bool, it could instead be remove_completers: str | list[str] of which completers to remove (text/word/local/variable, keyword, or snippet) - or other way around as include_completers?

see:

bouzidanas commented 3 months ago

Very very good catch @ewoo17 !! That is not intentional!

I like your proposition of remove_completers. I will try to get to it soon or alternatively you can make the changes, test them, then submit a pull request.

The relevant bit of code is:

if(replaceCompleter) {
          editorRef.current.editor.completers.pop();
}

in https://github.com/bouzidanas/streamlit-code-editor/blob/master/code_editor/frontend/src/editor.tsx

ewoo17 commented 3 months ago

I probably won't have the time at the moment to address this testing-wise although I could def submit a change for you to test if you'd like lol (would be my first time building the component locally too) but maybe for future issues! Not the most pressed with this issue as my completions would load above the local suggestions

bouzidanas commented 3 months ago

No worries. I will probably get this done late Friday. Thanks for looking into it and finding the source of the issue! Im solo developing this and many other projects in my free time so fixes can be a little slow at times.