Skip to content

Commit

Permalink
Merge pull request #94 from Bishoy-at-pieces/add-websocket-stream
Browse files Browse the repository at this point in the history
Add websocket stream
  • Loading branch information
bishoy-at-pieces authored Jun 25, 2024
2 parents 3d88750 + 43a0505 commit 78c74f2
Show file tree
Hide file tree
Showing 30 changed files with 1,136 additions and 208 deletions.
17 changes: 6 additions & 11 deletions Context.sublime-menu
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"children":
[
{
"caption": "Save to pieces",
"caption": "Save to Pieces",
"command": "pieces_create_asset",
},
{
Expand All @@ -14,22 +14,17 @@
{
"caption": "Fix a bug",
"command": "pieces_ask_question",
"args":{"question":"bug"}
"args":{"task":"fix"}
},
{
"caption": "Refactor",
"caption": "Modify",
"command": "pieces_ask_question",
"args":{"question":"refactor"}
"args":{"task":"modify"}
},
{
"caption":"Add a doc string to this function",
"caption":"Add code comments",
"command": "pieces_ask_question",
"args":{"question":"docstring"}
},
{
"caption": "Add comments to the code",
"command": "pieces_ask_question",
"args":{"question":"comment"}
"args":{"task":"comment"}
}
]
}
Expand Down
6 changes: 5 additions & 1 deletion Pieces.sublime-commands
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
"caption": "Pieces: Reload Plugin",
"command": "pieces_reload"
},
{
"caption": "Pieces: Copilot",
"command": "pieces_ask_stream"
},
{
"caption": "Pieces: Open Pieces OS",
"command": "pieces_open_pieces"
Expand All @@ -41,7 +45,7 @@
"caption": "Pieces: About",
"command": "pieces_about"
},
{
{
"caption": "Pieces: Open Pieces Settings",
"command": "edit_settings",
"args":
Expand Down
2 changes: 1 addition & 1 deletion ask/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
from .commands import PiecesAskQuestionCommand,ReplaceSelectionCommand
from .commands import PiecesAskQuestionCommand,PiecesReplaceCodeSelectionCommand
226 changes: 137 additions & 89 deletions ask/commands.py
Original file line number Diff line number Diff line change
@@ -1,51 +1,73 @@
import sublime
import sublime_plugin
from .._pieces_lib import pieces_os_client as pos_client
import textwrap
from .._pieces_lib.pieces_os_client import (
QGPTTaskPipelineForCodeFix,
QGPTTaskPipeline,
QGPTTaskPipelineForCodeCommentation,
QGPTTaskPipelineForCodeModification,
QGPTApi,
Seed,
SeededFormat,
SeededAsset,
SeededFragment,
RelevantQGPTSeed,
QGPTPromptPipeline,
RelevantQGPTSeeds,
TransferableString,
QGPTQuestionInput,
ClassificationSpecificEnum,
SeededClassification)
import re
from difflib import Differ
import mdpopups
import time

from .diff import show_diff_popup
from ..settings import PiecesSettings
from .prompts import *

prompt_map = {"bug":BUGS_PROMPT,
"refactor": CLEANER_CODE_PROMPT,
"docstring":DOC_STRING_PROMPT,
"comment":ADD_COMMENTS_PROMPT}



description_needed_commands = {
"modify":"Enter the instructions that should we use to modify that code",
"fix":"Enter the error message that you got"
}

class PiecesAskQuestionCommand(sublime_plugin.TextCommand):
def is_enabled(self):
return PiecesSettings().is_loaded


def run(self,edit, question):
sublime.set_timeout_async(lambda:self.run_async(edit,question),0)
def run(self,edit, task):
# task = comment,fix,modify
self.task = task
sublime.set_timeout_async(self.run_async,0)




def run_async(self,edit,question):
self.question = prompt_map[question]


def run_async(self):
# Get the current selection
self.selection = self.view.sel()[0]
self.selected_text = self.view.substr(self.selection)

# Modify the selection to whole line not part of it
self.selection = sublime.Region(self.view.line(self.selection.begin()).begin(),self.selection.end())
self.selected_text = textwrap.dedent(self.view.substr(self.selection))

# Getting the langauge
try:
self.langauge = self.view.file_name().split(".")[-1]
ext = self.view.file_name().split(".")[-1]

if ext in ClassificationSpecificEnum:
self.classification = SeededClassification(specific = ext)
else:
raise AttributeError
except:
self.langauge = "txt"
self.classification = None

if not self.selected_text:
sublime.error_message("Please select a text to ask about!")
return

if self.question in description_needed_commands:
sublime.active_window().show_input_panel("Enter a description:", "", self.on_done, None, None)
description_placeholder = description_needed_commands.get(self.task)

if description_placeholder:
sublime.active_window().show_input_panel(description_placeholder, "", self.on_done, None, None)
else:
self.on_done_async()

Expand All @@ -58,39 +80,51 @@ def on_done(self,description):
sublime.set_timeout_async(self.on_done_async,0)

def on_done_async(self):
self.view.set_status('Pieces Refactoring', 'Copilot is thinking...')

query = self.question.format(description=self.description,code=self.selected_text) if self.description else self.question.format(code=self.selected_text)
if self.task == "fix":
pipeline = QGPTTaskPipeline(code_fix=QGPTTaskPipelineForCodeFix(error=self.description))
elif self.task == "modify":
pipeline = QGPTTaskPipeline(code_modification=QGPTTaskPipelineForCodeModification(instruction=self.description))
elif self.task == "comment":
pipeline = QGPTTaskPipeline(code_commentation=QGPTTaskPipelineForCodeCommentation())

self.view.set_status('Pieces Refactoring', 'Copilot is thinking...')


res = pos_client.QGPTApi(PiecesSettings.api_client).question(
pos_client.QGPTQuestionInput(
query = query,
model = PiecesSettings.model_id,
relevant = pos_client.RelevantQGPTSeeds(
iterable = [
# TODO: Use the pipeline prompts
# pos_client.RelevantQGPTSeed(
# seed = pos_client.Seed(
# type="SEEDED_ASSET",
# asset=pos_client.SeededAsset(
# application=PiecesSettings.application,
# format=pos_client.SeededFormat(
# fragment = pos_client.SeededFragment(
# string = pos_client.TransferableString(raw = selected_text)
# ),
# ),
# ),
# ),
# )
]
)
gpt_input = QGPTQuestionInput(
query = " ",
model = PiecesSettings.model_id,
application = PiecesSettings.application.id,
pipeline = QGPTPromptPipeline(
task = pipeline
),
relevant = RelevantQGPTSeeds(
iterable = [
RelevantQGPTSeed(
seed = Seed(
type="SEEDED_ASSET",
asset=SeededAsset(
application=PiecesSettings.application,
format=SeededFormat(
fragment = SeededFragment(
string = TransferableString(raw = self.selected_text)
),
classification = self.classification
),
),
),
)
]
)
)

try:
res = QGPTApi(PiecesSettings.api_client).question(gpt_input)
except:
self.view.set_status('Pieces Refactoring', 'Copilot error in getting the responses')
sublime.set_timeout(lambda:self.view.erase_status("Pieces Refactoring"),5000)
return
self.view.set_status('Pieces Refactoring', 'Copilot analyzing...')
self.window = self.view.window()

response_code = res.answers.iterable[0].text

# Regular expression pattern for code block
Expand All @@ -100,50 +134,64 @@ def on_done_async(self):
match = re.search(pattern, response_code, re.DOTALL)
if match:
self.code = match.group(1)
self.code_html = self.get_differences(self.selected_text.splitlines(),self.code.splitlines())
link = "<a href=insert>✅ Accept</a> | <a href=dismiss style='color:red'>❌ Reject</a>"
html = f"<div style='display:inline-block'>{link}</div>{self.code_html}"

# Calculate the length of the code_html
code_html_length = len(self.code_html)

# Create a phantom at the end of the current selection
phantom_region = sublime.Region(self.selection.begin(), self.selection.begin() + code_html_length)
self.phantom = mdpopups.add_phantom(self.view,"code_phantom", phantom_region, html, sublime.LAYOUT_INLINE,md=False,on_navigate=self.on_nav)
show_diff_popup(self.view, self.selected_text.splitlines(), self.code.splitlines(),on_nav=self.on_nav)

self.is_done = True
self.view.erase_status('Pieces Refactoring')
else:
mdpopups.show_popup(self.view,response_code,md=True) # No code found
self.view.erase_status('Pieces Refactoring')


def on_nav(self, href):
if href == "insert":
# Replace the selected text with the code
self.view.run_command("replace_selection", {"code": self.code, "selection": [self.selection.a, self.selection.b]})
# Remove the phantom
mdpopups.erase_phantom_by_id(self.view,self.phantom)
self.view.run_command("pieces_replace_code_selection", {"code": self.code, "selection": [self.selection.a, self.selection.b]})
# Remove the popup
self.view.hide_popup()
elif href == "dismiss":
mdpopups.erase_phantom_by_id(self.view,self.phantom)




def get_differences(self,s1:list,s2:list):

# Compare the snippets
diffs = Differ().compare(s1, s2)

final_output = "\n".join(diffs)

final_output = mdpopups.md2html(self.view,f"```{self.langauge}\n{final_output}\n```")

return final_output


class ReplaceSelectionCommand(sublime_plugin.TextCommand):
def run(self, edit, code, selection):
# Convert the selection into a Region
region = sublime.Region(selection[0], selection[1])

# Replace the current selection with the provided code
self.view.replace(edit, region, code)

self.view.hide_popup()




class PiecesReplaceCodeSelectionCommand(sublime_plugin.TextCommand):
def run(self, edit, code, selection):
# Convert the selection into a Region
region = sublime.Region(selection[0], selection[1])

# Retrieve the settings for tabs vs. spaces and the number of spaces per tab
settings = self.view.settings()
use_spaces = settings.get('translate_tabs_to_spaces')
tab_size = settings.get('tab_size', 4)

# Get the current indentation level of the selected region
current_line_region = self.view.line(region.begin())
current_line_text = self.view.substr(current_line_region)
current_indentation = self._get_indentation(current_line_text, tab_size)

# Adjust the indentation of the replacement code
indented_code = self._adjust_indentation(code, current_indentation, use_spaces, tab_size)

# Replace the current selection with the indented code
self.view.replace(edit, region, indented_code)

def _get_indentation(self, line_text, tab_size):
"""Calculate the indentation level of the given line."""
indentation = 0
for char in line_text:
if char == '\t':
indentation += tab_size
elif char == ' ':
indentation += 1
else:
break
return indentation

def _adjust_indentation(self, code, indentation, use_spaces, tab_size):
"""Adjust the indentation of the given code."""
lines = code.split('\n')
indent_char = ' ' * tab_size if use_spaces else '\t'
indent_string = indent_char * (indentation // tab_size) + ' ' * (indentation % tab_size)
indented_lines = [indent_string + line if line.strip() else line for line in lines]
return '\n'.join(indented_lines)

Loading

0 comments on commit 78c74f2

Please sign in to comment.