diff options
author | Nate Sesti <sestinj@gmail.com> | 2023-06-21 14:02:56 -0700 |
---|---|---|
committer | Nate Sesti <sestinj@gmail.com> | 2023-06-21 14:02:56 -0700 |
commit | 94341653cae5b9af6e33f480847dfb562aa7578c (patch) | |
tree | 01e7541746d5d75d607e029db54bfda6ff54d16a | |
parent | a06fc2a82de1ca55ea6f748baa297cf6fbb57222 (diff) | |
download | sncontinue-94341653cae5b9af6e33f480847dfb562aa7578c.tar.gz sncontinue-94341653cae5b9af6e33f480847dfb562aa7578c.tar.bz2 sncontinue-94341653cae5b9af6e33f480847dfb562aa7578c.zip |
first slightly working version of suggestions
-rw-r--r-- | continuedev/src/continuedev/server/ide.py | 6 | ||||
-rw-r--r-- | continuedev/src/continuedev/server/ide_protocol.py | 2 | ||||
-rw-r--r-- | continuedev/src/continuedev/steps/core/core.py | 448 | ||||
-rw-r--r-- | extension/src/continueIdeClient.ts | 25 | ||||
-rw-r--r-- | extension/src/lang-server/codeLens.ts | 19 | ||||
-rw-r--r-- | extension/src/suggestions.ts | 60 | ||||
-rw-r--r-- | extension/src/util/vscode.ts | 20 |
7 files changed, 286 insertions, 294 deletions
diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py index c83fbc8a..c2ebaccf 100644 --- a/continuedev/src/continuedev/server/ide.py +++ b/continuedev/src/continuedev/server/ide.py @@ -146,8 +146,10 @@ class IdeProtocolServer(AbstractIdeProtocolServer): # ------------------------------- # # Request actions in IDE, doesn't matter which Session - def showSuggestion(): - pass + async def showSuggestion(self, file_edit: FileEdit): + await self._send_json("showSuggestion", { + "edit": file_edit.dict() + }) async def setFileOpen(self, filepath: str, open: bool = True): # Autopilot needs access to this. diff --git a/continuedev/src/continuedev/server/ide_protocol.py b/continuedev/src/continuedev/server/ide_protocol.py index 2dcedc30..79820c36 100644 --- a/continuedev/src/continuedev/server/ide_protocol.py +++ b/continuedev/src/continuedev/server/ide_protocol.py @@ -12,7 +12,7 @@ class AbstractIdeProtocolServer(ABC): """Handle a json message""" @abstractmethod - def showSuggestion(): + def showSuggestion(self, file_edit: FileEdit): """Show a suggestion to the user""" @abstractmethod diff --git a/continuedev/src/continuedev/steps/core/core.py b/continuedev/src/continuedev/steps/core/core.py index 71a5b5b2..eb6a00c6 100644 --- a/continuedev/src/continuedev/steps/core/core.py +++ b/continuedev/src/continuedev/steps/core/core.py @@ -159,253 +159,247 @@ class DefaultModelEditCodeStep(Step): self.name = await models.gpt35.complete(f"Write a very short title to describe this requested change: '{self.user_input}'. This is the title:") return f"`{self.user_input}`\n\n" + description - async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: - self.description = f"`{self.user_input}`" - await sdk.update_ui() - - rif_with_contents = [] - for range_in_file in map(lambda x: RangeInFile( - filepath=x.filepath, - # Only consider the range line-by-line. Maybe later don't if it's only a single line. - range=x.range.to_full_lines() - ), self.range_in_files): - file_contents = await sdk.ide.readRangeInFile(range_in_file) - rif_with_contents.append( - RangeInFileWithContents.from_range_in_file(range_in_file, file_contents)) - - rif_dict = {} - for rif in rif_with_contents: - rif_dict[rif.filepath] = rif.contents - - for rif in rif_with_contents: - await sdk.ide.setFileOpen(rif.filepath) - - model_to_use = sdk.models.default - - full_file_contents = await sdk.ide.readFile(rif.filepath) - - full_file_contents_lst = full_file_contents.split("\n") - - max_start_line = rif.range.start.line - min_end_line = rif.range.end.line - cur_start_line = 0 - cur_end_line = len(full_file_contents_lst) - 1 - - def cut_context(model_to_use, total_tokens, cur_start_line, cur_end_line): - - if total_tokens > MAX_TOKENS_FOR_MODEL[model_to_use.name]: - while cur_end_line > min_end_line: - total_tokens -= model_to_use.count_tokens( - full_file_contents_lst[cur_end_line]) - cur_end_line -= 1 - if total_tokens < MAX_TOKENS_FOR_MODEL[model_to_use.name]: - return cur_start_line, cur_end_line - - if total_tokens > MAX_TOKENS_FOR_MODEL[model_to_use.name]: - while cur_start_line < max_start_line: - cur_start_line += 1 - total_tokens -= model_to_use.count_tokens( - full_file_contents_lst[cur_end_line]) - if total_tokens < MAX_TOKENS_FOR_MODEL[model_to_use.name]: - return cur_start_line, cur_end_line - - return cur_start_line, cur_end_line - - if model_to_use.name == "gpt-4": - - total_tokens = model_to_use.count_tokens(full_file_contents + self._prompt) - cur_start_line, cur_end_line = cut_context( - model_to_use, total_tokens, cur_start_line, cur_end_line) - - elif model_to_use.name == "gpt-3.5-turbo" or model_to_use.name == "gpt-3.5-turbo-16k": - - if sdk.models.gpt35.count_tokens(full_file_contents) > MAX_TOKENS_FOR_MODEL["gpt-3.5-turbo"]: - - model_to_use = sdk.models.gpt3516k - total_tokens = model_to_use.count_tokens( - full_file_contents + self._prompt) - cur_start_line, cur_end_line = cut_context( - model_to_use, total_tokens, cur_start_line, cur_end_line) - - else: - - raise Exception("Unknown default model") + async def get_prompt_parts(self, rif: RangeInFileWithContents, sdk: ContinueSDK, full_file_contents: str): + # If using 3.5 and overflows, upgrade to 3.5.16k + model_to_use = sdk.models.default + if model_to_use.name == "gpt-3.5-turbo": + if sdk.models.gpt35.count_tokens(full_file_contents) > MAX_TOKENS_FOR_MODEL["gpt-3.5-turbo"]: + model_to_use = sdk.models.gpt3516k + + # Remove tokens from the end first, and then the start to clear space + # This part finds the start and end lines + full_file_contents_lst = full_file_contents.split("\n") + max_start_line = rif.range.start.line + min_end_line = rif.range.end.line + cur_start_line = 0 + cur_end_line = len(full_file_contents_lst) - 1 + + total_tokens = model_to_use.count_tokens( + full_file_contents + self._prompt) + + if total_tokens > MAX_TOKENS_FOR_MODEL[model_to_use.name]: + while cur_end_line > min_end_line: + total_tokens -= model_to_use.count_tokens( + full_file_contents_lst[cur_end_line]) + cur_end_line -= 1 + if total_tokens < MAX_TOKENS_FOR_MODEL[model_to_use.name]: + return cur_start_line, cur_end_line + + if total_tokens > MAX_TOKENS_FOR_MODEL[model_to_use.name]: + while cur_start_line < max_start_line: + cur_start_line += 1 + total_tokens -= model_to_use.count_tokens( + full_file_contents_lst[cur_end_line]) + if total_tokens < MAX_TOKENS_FOR_MODEL[model_to_use.name]: + return cur_start_line, cur_end_line + + # Now use the found start/end lines to get the prefix and suffix strings + file_prefix = "\n".join( + full_file_contents_lst[cur_start_line:max_start_line]) + file_suffix = "\n".join( + full_file_contents_lst[min_end_line:cur_end_line - 1]) + + # Move any surrounding blank line in rif.contents to the prefix/suffix + # TODO: Keep track of start line of the range, because it's needed below for offset stuff + rif_start_line = rif.range.start.line + if len(rif.contents) > 0: + first_line = rif.contents.splitlines(keepends=True)[0] + while first_line.strip() == "": + file_prefix += first_line + rif.contents = rif.contents[len(first_line):] + first_line = rif.contents.splitlines(keepends=True)[0] - code_before = "\n".join( - full_file_contents_lst[cur_start_line:max_start_line]) - code_after = "\n".join( - full_file_contents_lst[min_end_line:cur_end_line - 1]) + last_line = rif.contents.splitlines(keepends=True)[-1] + while last_line.strip() == "": + file_suffix = last_line + file_suffix + rif.contents = rif.contents[:len( + rif.contents) - len(last_line)] + last_line = rif.contents.splitlines(keepends=True)[-1] - segs = [code_before, code_after] - if segs[0].strip() == "": - segs[0] = segs[0].strip() - if segs[1].strip() == "": - segs[1] = segs[1].strip() + while rif.contents.startswith("\n"): + file_prefix += "\n" + rif.contents = rif.contents[1:] + while rif.contents.endswith("\n"): + file_suffix = "\n" + file_suffix + rif.contents = rif.contents[:-1] - # Move any surrounding blank line in rif.contents to the prefix/suffix - if len(rif.contents) > 0: - first_line = rif.contents.splitlines(keepends=True)[0] - while first_line.strip() == "": - segs[0] += first_line - rif.contents = rif.contents[len(first_line):] - first_line = rif.contents.splitlines(keepends=True)[0] + return file_prefix, rif.contents, file_suffix, model_to_use - last_line = rif.contents.splitlines(keepends=True)[-1] - while last_line.strip() == "": - segs[1] = last_line + segs[1] - rif.contents = rif.contents[:len( - rif.contents) - len(last_line)] - last_line = rif.contents.splitlines(keepends=True)[-1] - - while rif.contents.startswith("\n"): - segs[0] += "\n" - rif.contents = rif.contents[1:] - while rif.contents.endswith("\n"): - segs[1] = "\n" + segs[1] - rif.contents = rif.contents[:-1] - - # .format(code=rif.contents, user_request=self.user_input, file_prefix=segs[0], file_suffix=segs[1]) - prompt = self._prompt - if segs[0].strip() != "": - prompt += dedent(f""" + def compile_prompt(self, file_prefix: str, contents: str, file_suffix: str, sdk: ContinueSDK) -> str: + prompt = self._prompt + if file_prefix.strip() != "": + prompt += dedent(f""" <file_prefix> -{segs[0]} +{file_prefix} </file_prefix>""") - prompt += dedent(f""" + prompt += dedent(f""" <code_to_edit> -{rif.contents} +{contents} </code_to_edit>""") - if segs[1].strip() != "": - prompt += dedent(f""" + if file_suffix.strip() != "": + prompt += dedent(f""" <file_suffix> -{segs[1]} +{file_suffix} </file_suffix>""") - prompt += dedent(f""" + prompt += dedent(f""" <user_request> {self.user_input} </user_request> <modified_code_to_edit> """) - lines = [] - unfinished_line = "" - i = 0 - original_lines = rif.contents.split("\n") - - async def add_line(i: int, line: str): - if i == 0: - # First line indentation, because the model will assume that it is replacing in this way - line = original_lines[0].replace( - original_lines[0].strip(), "") + line - - if i < len(original_lines): - # Replace original line - range = Range.from_shorthand( - rif.range.start.line + i, rif.range.start.character if i == 0 else 0, rif.range.start.line + i + 1, 0) - else: - # Insert a line - range = Range.from_shorthand( - rif.range.start.line + i, 0, rif.range.start.line + i, 0) - - await sdk.ide.applyFileSystemEdit(FileEdit( - filepath=rif.filepath, - range=range, - replacement=line + "\n" - )) - - lines_of_prefix_copied = 0 - line_below_highlighted_range = segs[1].lstrip().split("\n")[0] - should_stop = False - async for chunk in model_to_use.stream_chat(prompt, with_history=await sdk.get_chat_context(), temperature=0): - if should_stop: + return prompt + + def is_end_line(self, line: str) -> bool: + return "</modified_code_to_edit>" in line + + def line_to_be_ignored(self, line: str) -> bool: + return "```" in line or "<modified_code_to_edit>" in line or "<file_prefix>" in line or "</file_prefix>" in line or "<file_suffix>" in line or "</file_suffix>" in line or "<user_request>" in line or "</user_request>" in line or "<code_to_edit>" in line or "</code_to_edit>" in line + + async def stream_rif(self, rif: RangeInFileWithContents, sdk: ContinueSDK): + full_file_contents = await sdk.ide.readFile(rif.filepath) + + file_prefix, contents, file_suffix, model_to_use = await self.get_prompt_parts( + rif, sdk, full_file_contents) + prompt = self.compile_prompt(file_prefix, contents, file_suffix, sdk) + + full_file_contents_lines = full_file_contents.split("\n") + original_lines = rif.contents.split("\n") + i = 0 + lines = [] + unfinished_line = "" + + current_block = [] + offset_from_blocks = 0 + + async def insert_line(line: str, line_no: int): + nonlocal current_block + # Insert line, highlight green, highlight corresponding line red + range = Range.from_shorthand( + line_no, 0, line_no, 0) + red_range = Range.from_shorthand( + line_no + len(current_block), 0, line_no + len(current_block), 0) + + await sdk.ide.applyFileSystemEdit(FileEdit( + filepath=rif.filepath, + range=range, + replacement=line + "\n" + )) + await sdk.ide.highlightCode(RangeInFile(filepath=rif.filepath, range=range), "#00FF0022") + await sdk.ide.highlightCode(RangeInFile(filepath=rif.filepath, range=red_range), "#FF000022") + + async def show_block_as_suggestion(): + nonlocal i, offset_from_blocks, current_block + await sdk.ide.showSuggestion(FileEdit( + filepath=rif.filepath, + range=Range.from_shorthand( + i + offset_from_blocks - len(current_block) + rif.range.start.line, 0, i + offset_from_blocks + rif.range.start.line, 0), + replacement="\n".join(current_block) + "\n" + )) + offset_from_blocks += len(current_block) + current_block.clear() + + async def add_to_block(line: str): + current_block.append(line) + # TODO: This start line might have changed above + # await insert_line(line, i + offset_from_blocks + + # rif.range.start.line) + + async def handle_generated_line(line: str): + nonlocal i, lines, current_block, offset_from_blocks, original_lines + # diff = list(difflib.ndiff(rif.contents.splitlines( + # keepends=True), completion.splitlines(keepends=True))) + if i < len(original_lines) and line == original_lines[i]: + # Line is the same as the original. Start a new block + await show_block_as_suggestion() + else: + # Add to the current block + await add_to_block(line) + + lines_of_prefix_copied = 0 + repeating_file_suffix = False + line_below_highlighted_range = file_suffix.lstrip().split("\n")[0] + async for chunk in model_to_use.stream_chat(prompt, with_history=await sdk.get_chat_context(), temperature=0): + # Stop early if it is repeating the file_suffix + if repeating_file_suffix: + break + + # Accumulate lines + chunk_lines = chunk.split("\n") + chunk_lines[0] = unfinished_line + chunk_lines[0] + if chunk.endswith("\n"): + unfinished_line = "" + chunk_lines.pop() # because this will be an empty string + else: + unfinished_line = chunk_lines.pop() + lines.extend(chunk_lines) + + # Deal with newly accumulated lines + for line in chunk_lines: + # Lines that should signify the end of generation + if self.is_end_line(line): + break + # Lines that should be ignored, like the <> tags + elif self.line_to_be_ignored(line): + continue + # Check if we are currently just copying the prefix + elif (lines_of_prefix_copied > 0 or i == 0) and lines_of_prefix_copied < len(file_prefix.splitlines()) and line == full_file_contents_lines[lines_of_prefix_copied]: + # This is a sketchy way of stopping it from repeating the file_prefix. Is a bug if output happens to have a matching line + lines_of_prefix_copied += 1 + continue + # Because really short lines might be expected to be repeated, this is only a !heuristic! + # Stop when it starts copying the file_suffix + elif line.strip() == line_below_highlighted_range.strip() and len(line.strip()) > 4: + repeating_file_suffix = True break - chunk_lines = chunk.split("\n") - chunk_lines[0] = unfinished_line + chunk_lines[0] - if chunk.endswith("\n"): - unfinished_line = "" - chunk_lines.pop() # because this will be an empty string - else: - unfinished_line = chunk_lines.pop() - lines.extend(chunk_lines) - - for line in chunk_lines: - if "</modified_code_to_edit>" in line: - break - elif "```" in line or "<modified_code_to_edit>" in line or "<file_prefix>" in line or "</file_prefix>" in line or "<file_suffix>" in line or "</file_suffix>" in line or "<user_request>" in line or "</user_request>" in line or "<code_to_edit>" in line or "</code_to_edit>" in line: - continue - elif (lines_of_prefix_copied > 0 or i == 0) and lines_of_prefix_copied < len(segs[0].splitlines()) and line == full_file_contents_lst[lines_of_prefix_copied]: - # This is a sketchy way of stopping it from repeating the file_prefix. Is a bug if output happens to have a matching line - lines_of_prefix_copied += 1 - continue - elif i < len(original_lines) and line == original_lines[i]: - i += 1 - continue - # Because really short lines might be expected to be repeated !heuristic! - elif line.strip() == line_below_highlighted_range.strip() and len(line.strip()) > 4: - should_stop = True - break - await add_line(i, line) - i += 1 - - # Add the unfinished line - if unfinished_line != "": - unfinished_line = unfinished_line.replace( - "</modified_code_to_edit>", "").replace("</code_to_edit>", "").replace("```", "").replace("</file_suffix>", "").replace("</file_prefix", "").replace( - "<modified_code_to_edit>", "").replace("<code_to_edit>", "").replace("<file_suffix>", "").replace("<file_prefix", "") - if not i < len(original_lines) or not unfinished_line == original_lines[i]: - await add_line(i, unfinished_line) - lines.append(unfinished_line) - i += 1 - # Remove the leftover original lines - while i < len(original_lines): - range = Range.from_shorthand( - rif.range.start.line + i, rif.range.start.character, rif.range.start.line + i, len(original_lines[i]) + 1) - await sdk.ide.applyFileSystemEdit(FileEdit( - filepath=rif.filepath, - range=range, - replacement="" - )) + # If none of the above, insert the line! + await handle_generated_line(line) i += 1 - completion = "\n".join(lines) - - self._prompt_and_completion += prompt + completion - - diff = list(difflib.ndiff(rif.contents.splitlines( - keepends=True), completion.splitlines(keepends=True))) - - lines_to_highlight = set() - index = 0 - for line in diff: - if line.startswith("-"): - pass - elif line.startswith("+"): - lines_to_highlight.add(index + rif.range.start.line) - index += 1 - elif line.startswith(" "): - index += 1 - - current_hl_start = None - last_hl = None - rifs_to_highlight = [] - for line in lines_to_highlight: - if current_hl_start is None: - current_hl_start = line - elif line != last_hl + 1: - rifs_to_highlight.append(RangeInFile( - filepath=rif.filepath, range=Range.from_shorthand(current_hl_start, 0, last_hl, 0))) - current_hl_start = line - last_hl = line - - if current_hl_start is not None: - rifs_to_highlight.append(RangeInFile( - filepath=rif.filepath, range=Range.from_shorthand(current_hl_start, 0, last_hl, 0))) - - for rif_to_hl in rifs_to_highlight: - await sdk.ide.highlightCode(rif_to_hl) - - await sdk.ide.saveFile(rif.filepath) + # Add the unfinished line + if unfinished_line != "" and not self.line_to_be_ignored(unfinished_line) and not self.is_end_line(unfinished_line): + lines.append(unfinished_line) + await handle_generated_line(unfinished_line) + i += 1 + + # Highlight the remainder of the range red + if i < len(original_lines): + await handle_generated_line("") + # range = Range.from_shorthand( + # i + 1 + offset_from_blocks + rif.range.start.line, 0, len(original_lines) + offset_from_blocks + rif.range.start.line, 0) + # await sdk.ide.highlightCode(RangeInFile(filepath=rif.filepath, range=range), "#FF000022") + + # If the current block isn't empty, add that suggestion + if len(current_block) > 0: + await show_block_as_suggestion() + + # Record the completion + completion = "\n".join(lines) + self._prompt_and_completion += prompt + completion + + async def run(self, sdk: ContinueSDK) -> Coroutine[Observation, None, None]: + self.description = f"`{self.user_input}`" + await sdk.update_ui() + + rif_with_contents = [] + for range_in_file in map(lambda x: RangeInFile( + filepath=x.filepath, + # Only consider the range line-by-line. Maybe later don't if it's only a single line. + range=x.range.to_full_lines() + ), self.range_in_files): + file_contents = await sdk.ide.readRangeInFile(range_in_file) + rif_with_contents.append( + RangeInFileWithContents.from_range_in_file(range_in_file, file_contents)) + + rif_dict = {} + for rif in rif_with_contents: + rif_dict[rif.filepath] = rif.contents + + for rif in rif_with_contents: + await sdk.ide.setFileOpen(rif.filepath) + await self.stream_rif(rif, sdk) + # await sdk.ide.saveFile(rif.filepath) class EditFileStep(Step): diff --git a/extension/src/continueIdeClient.ts b/extension/src/continueIdeClient.ts index fbad5f5d..fbd00c6e 100644 --- a/extension/src/continueIdeClient.ts +++ b/extension/src/continueIdeClient.ts @@ -1,5 +1,8 @@ // import { ShowSuggestionRequest } from "../schema/ShowSuggestionRequest"; -import { showSuggestion, SuggestionRanges } from "./suggestions"; +import { + showSuggestion as showSuggestionInEditor, + SuggestionRanges, +} from "./suggestions"; import { openEditorAndRevealRange, getRightViewColumn } from "./util/vscode"; import { FileEdit } from "../schema/FileEdit"; import { RangeInFile } from "../schema/RangeInFile"; @@ -115,7 +118,10 @@ class IdeProtocolClient { break; case "setFileOpen": this.openFile(data.filepath); - // TODO: Close file + // TODO: Close file if False + break; + case "showSuggestion": + this.showSuggestion(data.edit); break; case "openGUI": case "connected": @@ -157,25 +163,12 @@ class IdeProtocolClient { isWholeLine: true, }); editor.setDecorations(decorationType, [range]); - - // Listen for changes to cursor position and then remove the decoration (but keep for at least 2 seconds) - const allowRemoveHighlight = () => { - const cursorDisposable = vscode.window.onDidChangeTextEditorSelection( - (event) => { - if (event.textEditor.document.uri.fsPath === rangeInFile.filepath) { - cursorDisposable.dispose(); - editor.setDecorations(decorationType, []); - } - } - ); - }; - setTimeout(allowRemoveHighlight, 2000); } } showSuggestion(edit: FileEdit) { // showSuggestion already exists - showSuggestion( + showSuggestionInEditor( edit.filepath, new vscode.Range( edit.range.start.line, diff --git a/extension/src/lang-server/codeLens.ts b/extension/src/lang-server/codeLens.ts index 2a362b62..e8766c3c 100644 --- a/extension/src/lang-server/codeLens.ts +++ b/extension/src/lang-server/codeLens.ts @@ -13,8 +13,8 @@ class SuggestionsCodeLensProvider implements vscode.CodeLensProvider { } let codeLenses: vscode.CodeLens[] = []; - for (let suggestion of suggestions) { - let range = new vscode.Range( + for (const suggestion of suggestions) { + const range = new vscode.Range( suggestion.oldRange.start, suggestion.newRange.end ); @@ -57,16 +57,16 @@ class PytestCodeLensProvider implements vscode.CodeLensProvider { document: vscode.TextDocument, token: vscode.CancellationToken ): vscode.CodeLens[] | Thenable<vscode.CodeLens[]> { - let codeLenses: vscode.CodeLens[] = []; + const codeLenses: vscode.CodeLens[] = []; let lineno = 1; - let languageLibrary = getLanguageLibrary(document.fileName); + const languageLibrary = getLanguageLibrary(document.fileName); for (let line of document.getText().split("\n")) { if ( languageLibrary.lineIsFunctionDef(line) && languageLibrary.parseFunctionDefForName(line).startsWith("test_") ) { - let functionToTest = languageLibrary.parseFunctionDefForName(line); - let fileAndFunctionNameSpecifier = + const functionToTest = languageLibrary.parseFunctionDefForName(line); + const fileAndFunctionNameSpecifier = document.fileName + "::" + functionToTest; codeLenses.push( new vscode.CodeLens(new vscode.Range(lineno, 0, lineno, 1), { @@ -85,12 +85,13 @@ class PytestCodeLensProvider implements vscode.CodeLensProvider { const allCodeLensProviders: { [langauge: string]: vscode.CodeLensProvider[] } = { - python: [new SuggestionsCodeLensProvider(), new PytestCodeLensProvider()], + // python: [new SuggestionsCodeLensProvider(), new PytestCodeLensProvider()], + "*": [new SuggestionsCodeLensProvider()], }; export function registerAllCodeLensProviders(context: vscode.ExtensionContext) { - for (let language in allCodeLensProviders) { - for (let codeLensProvider of allCodeLensProviders[language]) { + for (const language in allCodeLensProviders) { + for (const codeLensProvider of allCodeLensProviders[language]) { context.subscriptions.push( vscode.languages.registerCodeLensProvider(language, codeLensProvider) ); diff --git a/extension/src/suggestions.ts b/extension/src/suggestions.ts index c66fad86..5ac6e095 100644 --- a/extension/src/suggestions.ts +++ b/extension/src/suggestions.ts @@ -14,7 +14,7 @@ export const editorToSuggestions: Map< string, // URI of file SuggestionRanges[] > = new Map(); -export let currentSuggestion: Map<string, number> = new Map(); // Map from editor URI to index of current SuggestionRanges in editorToSuggestions +export const currentSuggestion: Map<string, number> = new Map(); // Map from editor URI to index of current SuggestionRanges in editorToSuggestions // When tab is reopened, rerender the decorations: vscode.window.onDidChangeActiveTextEditor((editor) => { @@ -25,16 +25,16 @@ vscode.workspace.onDidOpenTextDocument((doc) => { rerenderDecorations(doc.uri.toString()); }); -let newDecorationType = vscode.window.createTextEditorDecorationType({ +const newDecorationType = vscode.window.createTextEditorDecorationType({ backgroundColor: "rgb(0, 255, 0, 0.1)", isWholeLine: true, }); -let oldDecorationType = vscode.window.createTextEditorDecorationType({ +const oldDecorationType = vscode.window.createTextEditorDecorationType({ backgroundColor: "rgb(255, 0, 0, 0.1)", isWholeLine: true, cursor: "pointer", }); -let newSelDecorationType = vscode.window.createTextEditorDecorationType({ +const newSelDecorationType = vscode.window.createTextEditorDecorationType({ backgroundColor: "rgb(0, 255, 0, 0.25)", isWholeLine: true, after: { @@ -42,7 +42,7 @@ let newSelDecorationType = vscode.window.createTextEditorDecorationType({ margin: "0 0 0 1em", }, }); -let oldSelDecorationType = vscode.window.createTextEditorDecorationType({ +const oldSelDecorationType = vscode.window.createTextEditorDecorationType({ backgroundColor: "rgb(255, 0, 0, 0.25)", isWholeLine: true, after: { @@ -52,19 +52,19 @@ let oldSelDecorationType = vscode.window.createTextEditorDecorationType({ }); export function rerenderDecorations(editorUri: string) { - let suggestions = editorToSuggestions.get(editorUri); - let idx = currentSuggestion.get(editorUri); - let editor = vscode.window.visibleTextEditors.find( + const suggestions = editorToSuggestions.get(editorUri); + const idx = currentSuggestion.get(editorUri); + const editor = vscode.window.visibleTextEditors.find( (editor) => editor.document.uri.toString() === editorUri ); if (!suggestions || !editor) return; - let olds: vscode.Range[] = [], - news: vscode.Range[] = [], - oldSels: vscode.Range[] = [], - newSels: vscode.Range[] = []; + const olds: vscode.Range[] = []; + const news: vscode.Range[] = []; + const oldSels: vscode.Range[] = []; + const newSels: vscode.Range[] = []; for (let i = 0; i < suggestions.length; i++) { - let suggestion = suggestions[i]; + const suggestion = suggestions[i]; if (typeof idx != "undefined" && idx === i) { if (suggestion.newSelected) { olds.push(suggestion.oldRange); @@ -92,14 +92,14 @@ export function rerenderDecorations(editorUri: string) { } export function suggestionDownCommand() { - let editor = vscode.window.activeTextEditor; + const editor = vscode.window.activeTextEditor; if (!editor) return; - let editorUri = editor.document.uri.toString(); - let suggestions = editorToSuggestions.get(editorUri); - let idx = currentSuggestion.get(editorUri); + const editorUri = editor.document.uri.toString(); + const suggestions = editorToSuggestions.get(editorUri); + const idx = currentSuggestion.get(editorUri); if (!suggestions || idx === undefined) return; - let suggestion = suggestions[idx]; + const suggestion = suggestions[idx]; if (!suggestion.newSelected) { suggestion.newSelected = true; } else if (idx + 1 < suggestions.length) { @@ -109,14 +109,14 @@ export function suggestionDownCommand() { } export function suggestionUpCommand() { - let editor = vscode.window.activeTextEditor; + const editor = vscode.window.activeTextEditor; if (!editor) return; - let editorUri = editor.document.uri.toString(); - let suggestions = editorToSuggestions.get(editorUri); - let idx = currentSuggestion.get(editorUri); + const editorUri = editor.document.uri.toString(); + const suggestions = editorToSuggestions.get(editorUri); + const idx = currentSuggestion.get(editorUri); if (!suggestions || idx === undefined) return; - let suggestion = suggestions[idx]; + const suggestion = suggestions[idx]; if (suggestion.newSelected) { suggestion.newSelected = false; } else if (idx > 0) { @@ -130,10 +130,10 @@ function selectSuggestion( accept: SuggestionSelectionOption, key: SuggestionRanges | null = null ) { - let editor = vscode.window.activeTextEditor; + const editor = vscode.window.activeTextEditor; if (!editor) return; - let editorUri = editor.document.uri.toString(); - let suggestions = editorToSuggestions.get(editorUri); + const editorUri = editor.document.uri.toString(); + const suggestions = editorToSuggestions.get(editorUri); if (!suggestions) return; @@ -218,14 +218,14 @@ export async function showSuggestion( range: vscode.Range, suggestion: string ): Promise<boolean> { - let existingCode = await readFileAtRange( + const existingCode = await readFileAtRange( new vscode.Range(range.start, range.end), editorFilename ); // If any of the outside lines are the same, don't repeat them in the suggestion - let slines = suggestion.split("\n"); - let elines = existingCode.split("\n"); + const slines = suggestion.split("\n"); + const elines = existingCode.split("\n"); let linesRemovedBefore = 0; let linesRemovedAfter = 0; while (slines.length > 0 && elines.length > 0 && slines[0] === elines[0]) { @@ -255,7 +255,7 @@ export async function showSuggestion( ) ); - let editor = await openEditorAndRevealRange(editorFilename, range); + const editor = await openEditorAndRevealRange(editorFilename, range); if (!editor) return Promise.resolve(false); return new Promise((resolve, reject) => { diff --git a/extension/src/util/vscode.ts b/extension/src/util/vscode.ts index a76b53c7..3110d589 100644 --- a/extension/src/util/vscode.ts +++ b/extension/src/util/vscode.ts @@ -118,9 +118,11 @@ export async function readFileAtRange( ) ); } else { - let firstLine = lines[range.start.line].slice(range.start.character); - let lastLine = lines[range.end.line].slice(0, range.end.character); - let middleLines = lines.slice(range.start.line + 1, range.end.line); + const firstLine = lines[range.start.line].slice( + range.start.character + ); + const lastLine = lines[range.end.line].slice(0, range.end.character); + const middleLines = lines.slice(range.start.line + 1, range.end.line); resolve([firstLine, ...middleLines, lastLine].join("\n")); } } @@ -144,7 +146,7 @@ export function openEditorAndRevealRange( setInterval(() => { resolve(null); }, 200); - }) + }); } showTextDocumentInProcess = true; vscode.window @@ -158,10 +160,10 @@ export function openEditorAndRevealRange( } resolve(editor); showTextDocumentInProcess = false; - }) - } catch (err) { - console.log(err); - } - }); + }); + } catch (err) { + console.log(err); + } + }); }); } |