diff --git a/pw_cli/py/BUILD.bazel b/pw_cli/py/BUILD.bazel index fd8d21574f..67320ffa73 100644 --- a/pw_cli/py/BUILD.bazel +++ b/pw_cli/py/BUILD.bazel @@ -30,6 +30,7 @@ py_library( "pw_cli/branding.py", "pw_cli/color.py", "pw_cli/decorators.py", + "pw_cli/diff.py", "pw_cli/env.py", "pw_cli/envparse.py", "pw_cli/file_filter.py", diff --git a/pw_cli/py/BUILD.gn b/pw_cli/py/BUILD.gn index 3c4680936c..32b7a3832a 100644 --- a/pw_cli/py/BUILD.gn +++ b/pw_cli/py/BUILD.gn @@ -31,6 +31,7 @@ pw_python_package("py") { "pw_cli/branding.py", "pw_cli/color.py", "pw_cli/decorators.py", + "pw_cli/diff.py", "pw_cli/env.py", "pw_cli/envparse.py", "pw_cli/file_filter.py", diff --git a/pw_cli/py/pw_cli/diff.py b/pw_cli/py/pw_cli/diff.py new file mode 100644 index 0000000000..ba142b6f21 --- /dev/null +++ b/pw_cli/py/pw_cli/diff.py @@ -0,0 +1,56 @@ +# Copyright 2024 The Pigweed Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +"""Print git-style diffs.""" + + +import difflib +import sys +from typing import Iterable, Sequence, Union + +from pw_cli.color import colors + +_COLOR = colors() + + +def colorize_diff_line(line: str) -> str: + if line.startswith('--- ') or line.startswith('+++ '): + return _COLOR.bold_white(line) + if line.startswith('-'): + return _COLOR.red(line) + if line.startswith('+'): + return _COLOR.green(line) + if line.startswith('@@ '): + return _COLOR.cyan(line) + return line + + +def colorize_diff(lines: Union[str, Iterable[str]]) -> str: + """Takes a diff str or list of str lines and returns a colorized version.""" + if isinstance(lines, str): + lines = lines.splitlines(True) + + return ''.join(colorize_diff_line(line) for line in lines) + + +def print_diff( + a: Sequence[str], b: Sequence[str], fromfile="", tofile="", indent=0 +) -> None: + """Print a git-style diff of two string sequences.""" + indent_str = ' ' * indent + diff = colorize_diff(difflib.unified_diff(a, b, fromfile, tofile)) + + for line in diff.splitlines(True): + sys.stdout.write(indent_str + line) + + sys.stdout.write("\n") diff --git a/pw_ide/guide/vscode/legacy.rst b/pw_ide/guide/vscode/legacy.rst index 58298493e7..4a54d0f353 100644 --- a/pw_ide/guide/vscode/legacy.rst +++ b/pw_ide/guide/vscode/legacy.rst @@ -30,7 +30,8 @@ be committed to source control. You should treat ``settings.json`` as a build artifact and avoid editing it directly. However, if you do make changes to it, don't worry! The changes - will be preserved after running ``pw ide sync`` + will be preserved after running ``pw ide sync`` if they don't conflict with + with the settings that command sets. The same pattern applies to ``tasks.json``, which provides Visual Studio Code tasks for ``pw_ide`` commands. Access these by opening the command palette diff --git a/pw_ide/py/pw_ide/commands.py b/pw_ide/py/pw_ide/commands.py index 47ccb8ccbf..f46387ee99 100644 --- a/pw_ide/py/pw_ide/commands.py +++ b/pw_ide/py/pw_ide/commands.py @@ -22,6 +22,7 @@ import sys from typing import cast, Set +from pw_cli.diff import print_diff from pw_cli.env import pigweed_environment from pw_cli.status_reporter import LoggingStatusReporter, StatusReporter @@ -252,19 +253,23 @@ def cmd_vscode( ) for settings_type in types_to_update: + prev_settings_str = '' prev_settings_hash = '' active_settings_existed = vsc_manager.active(settings_type).is_present() if active_settings_existed: - prev_settings_hash = vsc_manager.active(settings_type).hash() + prev_settings = vsc_manager.active(settings_type) + prev_settings_str = str(prev_settings) + prev_settings_hash = prev_settings.hash() with vsc_manager.active(settings_type).build() as active_settings: vsc_manager.default(settings_type).sync_to(active_settings) vsc_manager.project(settings_type).sync_to(active_settings) vsc_manager.user(settings_type).sync_to(active_settings) - vsc_manager.active(settings_type).sync_to(active_settings) - new_settings_hash = vsc_manager.active(settings_type).hash() + new_settings = vsc_manager.active(settings_type) + new_settings_str = str(new_settings) + new_settings_hash = new_settings.hash() settings_changed = new_settings_hash != prev_settings_hash _LOG.debug( @@ -284,6 +289,14 @@ def cmd_vscode( f'{verb} Visual Studio Code active ' f'{settings_type.value}' ) + print_diff( + prev_settings_str.splitlines(True), + new_settings_str.splitlines(True), + fromfile=f"{settings_type.value}.json", + tofile=f"{settings_type.value}.json", + indent=8, + ) + # Elements: # - The root search path of the compilation database diff --git a/pw_ide/py/pw_ide/editors.py b/pw_ide/py/pw_ide/editors.py index 7382cc1266..9539d5da6b 100644 --- a/pw_ide/py/pw_ide/editors.py +++ b/pw_ide/py/pw_ide/editors.py @@ -324,6 +324,9 @@ def __init__( def __repr__(self) -> str: return f'<{self.__class__.__name__}: (in memory)>' + def __str__(self) -> str: + return json.dumps(self.get(), indent=2) + def get(self) -> EditorSettingsDict: """Return the settings as an ordered dict.""" return self._data diff --git a/pw_module/py/pw_module/create.py b/pw_module/py/pw_module/create.py index 257194b032..edba041592 100644 --- a/pw_module/py/pw_module/create.py +++ b/pw_module/py/pw_module/create.py @@ -35,8 +35,8 @@ from pw_build import generate_modules_lists import pw_cli.color import pw_cli.env +from pw_cli.diff import colorize_diff from pw_cli.status_reporter import StatusReporter -from pw_presubmit.tools import colorize_diff from pw_module.templates import get_template diff --git a/pw_presubmit/py/pw_presubmit/format_code.py b/pw_presubmit/py/pw_presubmit/format_code.py index 6f22d29909..ed0fa8304e 100755 --- a/pw_presubmit/py/pw_presubmit/format_code.py +++ b/pw_presubmit/py/pw_presubmit/format_code.py @@ -42,6 +42,7 @@ ) import pw_cli.color +from pw_cli.diff import colorize_diff import pw_cli.env from pw_cli.file_filter import FileFilter from pw_cli.plural import plural @@ -69,7 +70,6 @@ file_summary, log_run, PresubmitToolRunner, - colorize_diff, ) from pw_presubmit.rst_format import reformat_rst diff --git a/pw_presubmit/py/pw_presubmit/keep_sorted.py b/pw_presubmit/py/pw_presubmit/keep_sorted.py index 0330f4ee5d..12926e51f2 100644 --- a/pw_presubmit/py/pw_presubmit/keep_sorted.py +++ b/pw_presubmit/py/pw_presubmit/keep_sorted.py @@ -29,6 +29,7 @@ ) import pw_cli +from pw_cli.diff import colorize_diff from pw_cli.plural import plural from . import cli, git_repo, presubmit, presubmit_context, tools @@ -385,7 +386,7 @@ def _process_files( ) outs.write(diff) - print(tools.colorize_diff(diff)) + print(colorize_diff(diff)) return errors diff --git a/pw_presubmit/py/pw_presubmit/python_checks.py b/pw_presubmit/py/pw_presubmit/python_checks.py index f9bd5cc8e0..b0c8f73912 100644 --- a/pw_presubmit/py/pw_presubmit/python_checks.py +++ b/pw_presubmit/py/pw_presubmit/python_checks.py @@ -25,6 +25,7 @@ import sys from tempfile import TemporaryDirectory +from pw_cli.diff import colorize_diff_line from pw_env_setup import python_packages from pw_presubmit.presubmit import ( @@ -37,7 +38,7 @@ PresubmitFailure, ) from pw_presubmit import build -from pw_presubmit.tools import log_run, colorize_diff_line +from pw_presubmit.tools import log_run _LOG = logging.getLogger(__name__) diff --git a/pw_presubmit/py/pw_presubmit/rst_format.py b/pw_presubmit/py/pw_presubmit/rst_format.py index 490bd6c413..9d1576c5dd 100644 --- a/pw_presubmit/py/pw_presubmit/rst_format.py +++ b/pw_presubmit/py/pw_presubmit/rst_format.py @@ -23,7 +23,7 @@ import textwrap from typing import Iterable -from pw_presubmit.tools import colorize_diff +from pw_cli.diff import colorize_diff TAB_WIDTH = 8 # Number of spaces to use for \t replacement CODE_BLOCK_INDENTATION = 3 diff --git a/pw_presubmit/py/pw_presubmit/tools.py b/pw_presubmit/py/pw_presubmit/tools.py index cc9d662cb7..157555030b 100644 --- a/pw_presubmit/py/pw_presubmit/tools.py +++ b/pw_presubmit/py/pw_presubmit/tools.py @@ -28,33 +28,11 @@ Pattern, ) -import pw_cli.color from pw_cli.plural import plural from pw_cli.tool_runner import ToolRunner from pw_presubmit.presubmit_context import PRESUBMIT_CONTEXT _LOG: logging.Logger = logging.getLogger(__name__) -_COLOR = pw_cli.color.colors() - - -def colorize_diff_line(line: str) -> str: - if line.startswith('--- ') or line.startswith('+++ '): - return _COLOR.bold_white(line) - if line.startswith('-'): - return _COLOR.red(line) - if line.startswith('+'): - return _COLOR.green(line) - if line.startswith('@@ '): - return _COLOR.cyan(line) - return line - - -def colorize_diff(lines: Iterable[str]) -> str: - """Takes a diff str or list of str lines and returns a colorized version.""" - if isinstance(lines, str): - lines = lines.splitlines(True) - - return ''.join(colorize_diff_line(line) for line in lines) def make_box(section_alignments: Sequence[str]) -> str: