Skip to content

Commit

Permalink
initial
Browse files Browse the repository at this point in the history
  • Loading branch information
daimor committed Sep 23, 2024
0 parents commit 9de583e
Show file tree
Hide file tree
Showing 53 changed files with 4,011 additions and 0 deletions.
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
venv
env
.env
.eggs
*.egg-info
build
dist
__pycache__
.pytest_cache
*.log
.coverage
coverage.xml
10 changes: 10 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"objectscript.conn": {
"active": false,
"host": "localhost",
"ns": "USER",
"port": 8273,
"username": "_SYSTEM",
"password": "SYS"
}
}
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 Dmitry Maslennikov

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# iTerm

Original IRIS Terminal as web application, which can run shell commands and python

```shell
zpm "install iterm"
```

![iTerm](https://raw.githubusercontent.com/caretdev/iterm/main/images/Screenshot1.png)

## Demo tools

To test how iterm works in browser, added to routines when running in docker

term routine to test colors, formatting and special symbols

```objectscript
do ^term
```

clock routine to test some alive application with positioning

```objectscript
do ^clock
```

![iTerm](https://raw.githubusercontent.com/caretdev/iterm/main/images/Screenshot2.png)
13 changes: 13 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
services:
iris:
image: intersystemsdc/iris-community:latest-preview-zpm
volumes:
- ./:/home/irisowner/iterm
ports:
- 8273:52773
working_dir: /home/irisowner/iterm
environment:
- IRIS_USERNAME=_SYSTEM
- IRIS_PASSWORD=SYS
command:
-a /home/irisowner/iterm/init-dev.sh
Binary file added images/Screenshot1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/Screenshot2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions init-dev.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash

SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )

cd $SCRIPT_DIR
pip install -e .

cat <<"EOF" | iris session iris -U %SYS
zpm "load -v /home/irisowner/iterm"
halt
EOF

cat <<"EOF" | iris session iris -U USER
do $system.OBJ.Load("/home/irisowner/iterm/src/clock.mac", "ck")
do $system.OBJ.Load("/home/irisowner/iterm/src/term.mac", "ck")
halt
EOF
1 change: 1 addition & 0 deletions iterm/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__version__ = "0.1.0"
9 changes: 9 additions & 0 deletions iterm/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""
iterm package main entry point
"""

from .main import cli


if __name__ == "__main__":
cli()
44 changes: 44 additions & 0 deletions iterm/clitoolbar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from __future__ import unicode_literals

from prompt_toolkit.key_binding.vi_state import InputMode
from prompt_toolkit.enums import EditingMode
from prompt_toolkit.application import get_app


def create_toolbar_tokens_func(cli):
"""
Return a function that generates the toolbar tokens.
"""

def get_toolbar_tokens():
result = []
result.append(("class:bottom-toolbar", " "))

if cli.multi_line:
result.append(
("class:bottom-toolbar", " (Semi-colon [;] will end the line) ")
)

if cli.multi_line:
result.append(("class:bottom-toolbar.on", "[F3] Multiline: ON "))
else:
result.append(("class:bottom-toolbar.off", "[F3] Multiline: OFF "))
if cli.prompt_app.editing_mode == EditingMode.VI:
result.append(
("class:botton-toolbar.on", "Vi-mode ({})".format(_get_vi_mode()))
)

return result

return get_toolbar_tokens


def _get_vi_mode():
"""Get the current vi mode for display."""
return {
InputMode.INSERT: "I",
InputMode.NAVIGATION: "N",
InputMode.REPLACE: "R",
InputMode.INSERT_MULTIPLE: "M",
InputMode.REPLACE_SINGLE: "R",
}[get_app().vi_state.input_mode]
58 changes: 58 additions & 0 deletions iterm/completer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import logging
from re import compile, escape

from prompt_toolkit.completion import Completer, Completion

# from .packages.completion_engine import suggest_type
# from .packages.parseutils import last_word
# from .packages.special.iocommands import favoritequeries
# from .packages.filepaths import parse_path, complete_path, suggest_path

_logger = logging.getLogger(__name__)


class IRISCompleter(Completer):
keywords = [
"set",
"for",
"read",
"open",
"use",
"close",
"while",
"merge",
]

variables = [
"$HOROLOG",
"$JOB",
"$NAMESPACE",
"$TLEVEL",
"$USERNAME",
"$ZHOROLOG",
"$ZJOB",
"$ZPI",
"$ZTIMESTAMP",
"$ZTIMEZONE",
"$ZVERSION",
]

def __init__(self, keyword_casing="auto"):
super(self.__class__, self).__init__()
self.reserved_words = set()
for x in self.keywords:
self.reserved_words.update(x.split())
self.name_pattern = compile(r"^[_a-z][_a-z0-9\$]*$")

self.special_commands = []
if keyword_casing not in ("upper", "lower", "auto"):
keyword_casing = "auto"
self.keyword_casing = keyword_casing
# self.reset_completions()

def get_completions(self, document, complete_event):
word_before_cursor = document.get_word_before_cursor(WORD=True)
completions = []
suggestions = []

return completions
130 changes: 130 additions & 0 deletions iterm/completion_refresher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import threading
from .packages.special.main import COMMANDS
from collections import OrderedDict

from .sqlcompleter import SQLCompleter
from .sqlexecute import SQLExecute


class CompletionRefresher(object):

refreshers = OrderedDict()

def __init__(self):
self._completer_thread = None
self._restart_refresh = threading.Event()

def refresh(self, executor, callbacks, completer_options=None):

"""Creates a SQLCompleter object and populates it with the relevant
completion suggestions in a background thread.
executor - SQLExecute object, used to extract the credentials to connect
to the database.
callbacks - A function or a list of functions to call after the thread
has completed the refresh. The newly created completion
object will be passed in as an argument to each callback.
completer_options - dict of options to pass to SQLCompleter.
"""
if completer_options is None:
completer_options = {}

if self.is_refreshing():
self._restart_refresh.set()
return [(None, None, None, "Auto-completion refresh restarted.")]
else:
self._completer_thread = threading.Thread(
target=self._bg_refresh,
args=(executor, callbacks, completer_options),
name="completion_refresh",
)
self._completer_thread.setDaemon(True)
self._completer_thread.start()
return [
(
None,
None,
None,
"Auto-completion refresh started in the background.",
)
]

def is_refreshing(self):
return self._completer_thread and self._completer_thread.is_alive()

def _bg_refresh(self, sqlexecute, callbacks, completer_options):
completer = SQLCompleter(**completer_options)

e = sqlexecute
# Create a new sqlexecute method to populate the completions.
executor = SQLExecute(
hostname=e.hostname,
port=e.port,
namespace=e.namespace,
username=e.username,
password=e.password,
embedded=e.embedded,
sslcontext=e.sslcontext,
**e.extra_params,
)

# If callbacks is a single function then push it into a list.
if callable(callbacks):
callbacks = [callbacks]

while 1:
for refresher in self.refreshers.values():
refresher(completer, executor)
if self._restart_refresh.is_set():
self._restart_refresh.clear()
break
else:
# Break out of while loop if the for loop finishes naturally
# without hitting the break statement.
break

# Start over the refresh from the beginning if the for loop hit the
# break statement.
continue

for callback in callbacks:
callback(completer)


def refresher(name, refreshers=CompletionRefresher.refreshers):
"""Decorator to add the decorated function to the dictionary of
refreshers. Any function decorated with a @refresher will be executed as
part of the completion refresh routine."""

def wrapper(wrapped):
refreshers[name] = wrapped
return wrapped

return wrapper


# @refresher("databases")
# def refresh_databases(completer, executor):
# completer.extend_database_names(executor.databases())


@refresher("schemas")
def refresh_schemata(completer, executor):
completer.extend_schemas(executor.schemas(), kind="tables")


@refresher("tables")
def refresh_tables(completer, executor):
completer.extend_relations(executor.tables(), kind="tables")
completer.extend_columns(executor.table_columns(), kind="tables")


# @refresher("functions")
# def refresh_functions(completer, executor):
# completer.extend_functions(executor.functions())


@refresher("special_commands")
def refresh_special(completer, executor):
completer.extend_special_commands(COMMANDS.keys())
Loading

0 comments on commit 9de583e

Please sign in to comment.