├── .gitignore
├── .idea
├── .gitignore
├── vcs.xml
├── modules.xml
├── code.iml
├── libraries
│ └── Dart_SDK.xml
└── codeStyles
│ └── Project.xml
├── org.purplegraphite.code
├── lib
│ ├── src
│ │ ├── common
│ │ │ ├── assets.dart
│ │ │ ├── constants.dart
│ │ │ ├── ui.dart
│ │ │ ├── routing_const.dart
│ │ │ ├── strings.dart
│ │ │ └── routes.dart
│ │ ├── models
│ │ │ ├── hive
│ │ │ │ ├── latest.dart
│ │ │ │ ├── settings
│ │ │ │ │ ├── editorSettings.dart
│ │ │ │ │ ├── consoleSettings.dart
│ │ │ │ │ ├── generalSettings.dart
│ │ │ │ │ ├── generalSettings.g.dart
│ │ │ │ │ ├── themeSettings.g.dart
│ │ │ │ │ └── themeSettings.dart
│ │ │ │ ├── history.dart
│ │ │ │ ├── repository.dart
│ │ │ │ └── history.g.dart
│ │ │ ├── plain_model
│ │ │ │ ├── ThemeColors.dart
│ │ │ │ └── entity.dart
│ │ │ ├── view_model
│ │ │ │ ├── browser_controller.dart
│ │ │ │ └── terminal_controller.dart
│ │ │ └── provider
│ │ │ │ └── history.dart
│ │ ├── utils
│ │ │ ├── string.dart
│ │ │ ├── logman.dart
│ │ │ ├── theme.dart
│ │ │ ├── checksum.dart
│ │ │ ├── fileutils.dart
│ │ │ └── permissions.dart
│ │ └── ui
│ │ │ ├── screens
│ │ │ ├── start
│ │ │ │ └── controller.dart
│ │ │ ├── history.dart
│ │ │ ├── terminal.dart
│ │ │ └── editor_tab.dart
│ │ │ └── components
│ │ │ ├── app_title.dart
│ │ │ ├── about
│ │ │ ├── version.dart
│ │ │ └── about.dart
│ │ │ ├── popup_menu_tile.dart
│ │ │ ├── acrylic.dart
│ │ │ ├── buttons
│ │ │ └── action_tabs_button.dart
│ │ │ ├── ask_dialog.dart
│ │ │ ├── newfolder_dialog.dart
│ │ │ ├── drawer
│ │ │ └── editor_drawer.dart
│ │ │ └── start_tips
│ │ │ └── tips.dart
│ ├── service
│ │ └── saver.dart
│ └── main.dart
├── android
│ ├── settings_aar.gradle
│ ├── gradle.properties
│ ├── app
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── res
│ │ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── drawable
│ │ │ │ │ │ └── launch_background.xml
│ │ │ │ │ └── values
│ │ │ │ │ │ └── styles.xml
│ │ │ │ ├── kotlin
│ │ │ │ │ └── org
│ │ │ │ │ │ └── purplegraphite
│ │ │ │ │ │ └── code
│ │ │ │ │ │ └── MainActivity.kt
│ │ │ │ └── AndroidManifest.xml
│ │ │ ├── debug
│ │ │ │ └── AndroidManifest.xml
│ │ │ └── profile
│ │ │ │ └── AndroidManifest.xml
│ │ └── build.gradle
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ ├── .gitignore
│ ├── settings.gradle
│ └── build.gradle
├── analysis_options.yaml
├── assets
│ └── fonts
│ │ └── Source_Code_Pro
│ │ ├── SourceCodePro-Bold.ttf
│ │ ├── SourceCodePro-Black.ttf
│ │ ├── SourceCodePro-Italic.ttf
│ │ ├── SourceCodePro-Light.ttf
│ │ ├── SourceCodePro-Medium.ttf
│ │ ├── SourceCodePro-Regular.ttf
│ │ ├── SourceCodePro-SemiBold.ttf
│ │ ├── SourceCodePro-BlackItalic.ttf
│ │ ├── SourceCodePro-BoldItalic.ttf
│ │ ├── SourceCodePro-ExtraLight.ttf
│ │ ├── SourceCodePro-LightItalic.ttf
│ │ ├── SourceCodePro-MediumItalic.ttf
│ │ ├── SourceCodePro-SemiBoldItalic.ttf
│ │ ├── SourceCodePro-ExtraLightItalic.ttf
│ │ └── OFL.txt
├── .metadata
├── .gitignore
├── test
│ └── widget_test.dart
└── pubspec.yaml
├── packages
└── creamy_field
│ ├── screenshots
│ ├── flutter_01.png
│ ├── flutter_02.png
│ └── flutter_03.png
│ ├── analysis_options.yaml
│ ├── .metadata
│ ├── example
│ ├── .metadata
│ ├── test
│ │ └── widget_test.dart
│ ├── README.md
│ ├── pubspec.yaml
│ ├── analysis_options.yaml
│ ├── .gitignore
│ ├── lib
│ │ └── main.dart
│ └── pubspec.lock
│ ├── pubspec.yaml
│ ├── lib
│ ├── src
│ │ ├── syntax_highlighter.dart
│ │ ├── syntax_highlighter
│ │ │ ├── dummy_syntax_highlighter.dart
│ │ │ ├── language_type.dart
│ │ │ └── creamy_syntax_highlighter.dart
│ │ ├── text_tools
│ │ │ ├── extensions.dart
│ │ │ └── toolbar_options.dart
│ │ └── decoration
│ │ │ └── horizontal_scrollable.dart
│ └── creamy_field.dart
│ ├── LICENSE
│ ├── CHANGELOG.md
│ ├── .gitignore
│ ├── README.md
│ ├── pubspec.lock
│ └── test
│ └── creamy_field_test.dart
├── CHANGELOG.md
├── tools
├── tag_version.sh
├── build_release.sh
├── status.sh
├── support
│ └── snr.py
└── update_version.sh
├── .github
├── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
└── workflows
│ ├── master.yml
│ ├── release.yml
│ ├── main_package.yml
│ └── main.yml
├── LICENSE.md
├── CODE_OF_CONDUCT.md
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | secrets
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /workspace.xml
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/common/assets.dart:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/android/settings_aar.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/models/hive/latest.dart:
--------------------------------------------------------------------------------
1 | // Unimplemented
2 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/models/hive/settings/editorSettings.dart:
--------------------------------------------------------------------------------
1 | // Unimplemented
2 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/models/hive/settings/consoleSettings.dart:
--------------------------------------------------------------------------------
1 | // Unimplemented
2 |
--------------------------------------------------------------------------------
/packages/creamy_field/screenshots/flutter_01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/predatorx7/snake_code/HEAD/packages/creamy_field/screenshots/flutter_01.png
--------------------------------------------------------------------------------
/packages/creamy_field/screenshots/flutter_02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/predatorx7/snake_code/HEAD/packages/creamy_field/screenshots/flutter_02.png
--------------------------------------------------------------------------------
/packages/creamy_field/screenshots/flutter_03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/predatorx7/snake_code/HEAD/packages/creamy_field/screenshots/flutter_03.png
--------------------------------------------------------------------------------
/org.purplegraphite.code/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | include: package:pedantic/analysis_options.yaml
2 |
3 | linter:
4 | rules:
5 | lines_longer_than_80_chars: ignore
--------------------------------------------------------------------------------
/org.purplegraphite.code/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.enableR8=true
3 | android.useAndroidX=true
4 | android.enableJetifier=true
5 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/predatorx7/snake_code/HEAD/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-Bold.ttf
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/utils/string.dart:
--------------------------------------------------------------------------------
1 | class StringUtils {
2 | static String toFirstLetterUppercase(String word) {
3 | return '${word[0].toUpperCase()}${word.substring(1)}';
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/predatorx7/snake_code/HEAD/org.purplegraphite.code/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/org.purplegraphite.code/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/predatorx7/snake_code/HEAD/org.purplegraphite.code/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/org.purplegraphite.code/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/predatorx7/snake_code/HEAD/org.purplegraphite.code/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/org.purplegraphite.code/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/predatorx7/snake_code/HEAD/org.purplegraphite.code/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/predatorx7/snake_code/HEAD/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-Black.ttf
--------------------------------------------------------------------------------
/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/predatorx7/snake_code/HEAD/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-Italic.ttf
--------------------------------------------------------------------------------
/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/predatorx7/snake_code/HEAD/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-Light.ttf
--------------------------------------------------------------------------------
/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/predatorx7/snake_code/HEAD/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-Medium.ttf
--------------------------------------------------------------------------------
/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/predatorx7/snake_code/HEAD/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-Regular.ttf
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/common/constants.dart:
--------------------------------------------------------------------------------
1 | /// The duration of the horizontal scroll animation that occurs when a tab is tapped.
2 | const Duration kTabScrollDuration = Duration(milliseconds: 300);
3 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/predatorx7/snake_code/HEAD/org.purplegraphite.code/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/predatorx7/snake_code/HEAD/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-SemiBold.ttf
--------------------------------------------------------------------------------
/packages/creamy_field/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | include: package:flutter_lints/flutter.yaml
2 |
3 | # Additional information about this file can be found at
4 | # https://dart.dev/guides/language/analysis-options
5 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/predatorx7/snake_code/HEAD/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-BlackItalic.ttf
--------------------------------------------------------------------------------
/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/predatorx7/snake_code/HEAD/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-BoldItalic.ttf
--------------------------------------------------------------------------------
/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-ExtraLight.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/predatorx7/snake_code/HEAD/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-ExtraLight.ttf
--------------------------------------------------------------------------------
/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/predatorx7/snake_code/HEAD/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-LightItalic.ttf
--------------------------------------------------------------------------------
/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/predatorx7/snake_code/HEAD/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-MediumItalic.ttf
--------------------------------------------------------------------------------
/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-SemiBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/predatorx7/snake_code/HEAD/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-SemiBoldItalic.ttf
--------------------------------------------------------------------------------
/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-ExtraLightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/predatorx7/snake_code/HEAD/org.purplegraphite.code/assets/fonts/Source_Code_Pro/SourceCodePro-ExtraLightItalic.ttf
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/utils/logman.dart:
--------------------------------------------------------------------------------
1 | import 'package:logging/logging.dart';
2 |
3 | /// An instance of [Logger] for logging debug development details on console.
4 | final logger = Logger(
5 | 'logger',
6 | );
7 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/android/app/src/main/kotlin/org/purplegraphite/code/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package org.purplegraphite.code
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/utils/theme.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | /// Is the current theme brighness is dark?
4 | bool isDarkTheme(BuildContext context) {
5 | return Theme.of(context).brightness == Brightness.dark;
6 | }
7 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## v0.5.0-aplha+2104101
4 |
5 | - Feature complete for phase 1
6 |
7 | ## Upcoming
8 |
9 | ## v0.1.0
10 |
11 | ### Version semantic
12 |
13 | - `version + hotfix.` for stable builds.
14 | - `version - pre-release + ` for any pre-release builds.
15 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: f7a6a7906be96d2288f5d63a5a54c515a6e987fe
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/packages/creamy_field/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: b712a172f9694745f50505c93340883493b505e5
8 | channel: stable
9 |
10 | project_type: package
11 |
--------------------------------------------------------------------------------
/packages/creamy_field/example/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: b712a172f9694745f50505c93340883493b505e5
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/models/plain_model/ThemeColors.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart' show MaterialColor;
2 |
3 | class ThemeStyle {
4 | /// Primary MaterialColor of this theme
5 | final MaterialColor color;
6 |
7 | /// Theme name, unique to every theme
8 | final String name;
9 |
10 | const ThemeStyle(this.color, this.name);
11 |
12 | @override
13 | String toString() => name;
14 | @override
15 | int get hashCode => name.hashCode + color.hashCode;
16 | }
17 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/ui/screens/start/controller.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/widgets.dart';
2 |
3 | enum PreviousProjectLoadingStatus { loading, empty, done }
4 |
5 | class StartScreenController with ChangeNotifier {
6 | PreviousProjectLoadingStatus _previousProjectStatus =
7 | PreviousProjectLoadingStatus.loading;
8 | PreviousProjectLoadingStatus get status =>
9 | _previousProjectStatus ?? PreviousProjectLoadingStatus.empty;
10 |
11 | StartScreenController();
12 | }
13 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/tools/tag_version.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | MAIN_PUBSPEC="org.purplegraphite.code/pubspec.yaml"
3 | CURRENT_VERSION="$(sed -n -e 's/^.*version: //p' ${MAIN_PUBSPEC})"
4 |
5 | echo "Tagging version: $CURRENT_VERSION"
6 |
7 | printf "\nAre you sure you have updated project's version,\nCHANGELOG.md, README.md, Documentation?\n"
8 | read -p "Continue? (Y/n) " -n 1 -r
9 | echo
10 | if [[ $REPLY =~ ^[Nn]$ ]]
11 | then
12 | exit 0
13 | fi
14 |
15 | set -x #echo on
16 |
17 | git tag -a v$CURRENT_VERSION -m "Code $CURRENT_VERSION"
18 |
19 | git push origin v$CURRENT_VERSION
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/models/hive/settings/generalSettings.dart:
--------------------------------------------------------------------------------
1 | import 'package:hive/hive.dart';
2 |
3 | part 'generalSettings.g.dart';
4 |
5 | @HiveType(typeId: 1)
6 | class GeneralSettings extends HiveObject {
7 | @HiveField(1)
8 | bool _debuggingEnabled;
9 |
10 | bool get debuggingEnabled => _debuggingEnabled ?? false;
11 |
12 | void set debuggingEnabled(bool debuggingEnabled) {
13 | if (_debuggingEnabled) {
14 | print('Cannot disable debugging');
15 | return;
16 | }
17 | _debuggingEnabled = debuggingEnabled;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/packages/creamy_field/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: creamy_field
2 | description: Rich Text Editing Field & other components with rich text, selection toolbar & syntax highlight support. Useful in Rich text editors.
3 | version: 0.4.1
4 | homepage: https://github.com/predatorx7/snake_code/tree/packages/creamy_field/packages/creamy_field
5 |
6 | environment:
7 | sdk: '>=2.15.0 <3.0.0'
8 | flutter: '>=2.8.0'
9 |
10 | dependencies:
11 | flutter:
12 | sdk: flutter
13 | flutter_highlighter: ^0.1.1
14 | highlighter: ^0.1.1
15 |
16 | dev_dependencies:
17 | flutter_test:
18 | sdk: flutter
19 | flutter_lints: ^1.0.0
20 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/common/ui.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class Corners {
4 | /// The border radius of Card & Material widgets in this application.
5 | ///
6 | /// The value is same as [BorderRadius.circular(25.0)].
7 | static const BorderRadius borderRadius =
8 | const BorderRadius.all(Radius.circular(25.0));
9 | static const ShapeBorder outlinedShapeBorder = RoundedRectangleBorder(
10 | side: BorderSide(
11 | color: Colors.black,
12 | width: 2,
13 | ),
14 | borderRadius: const BorderRadius.all(
15 | Radius.circular(25),
16 | ),
17 | );
18 | }
19 |
--------------------------------------------------------------------------------
/tools/build_release.sh:
--------------------------------------------------------------------------------
1 | export KEY_ALIAS=$1
2 | export STORE_PASSWORD=$2
3 | echo "Building release.."
4 | if [ -z "$1" ] || [ -z "$2" ]; then
5 | echo
6 | echo "KEY_ALIAS or STORE_PASSWORD has not been specified."
7 | echo "Usage: tools/build_release.sh KEY_ALIAS STORE_PASSWORD"
8 | exit 1
9 | fi
10 | if [ -e org.purplegraphite.code/release.jks ]
11 | then
12 | echo
13 | else
14 | echo
15 | echo "Release keystore file \"org.purplegraphite.code/release.jks\" doesn't exist."
16 | exit 1
17 | fi
18 | cd org.purplegraphite.code;
19 | flutter build apk --release --split-per-abi --split-debug-info=output/symbols;
20 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/ui/components/app_title.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:google_fonts/google_fonts.dart';
3 |
4 | class ApplicationTitle extends StatelessWidget {
5 | final bool showDark;
6 |
7 | const ApplicationTitle({Key key, @required this.showDark}) : super(key: key);
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | return Text(
12 | 'Snake code',
13 | style: GoogleFonts.openSans(
14 | fontSize: 28,
15 | fontWeight: FontWeight.w800,
16 | color: showDark ? Colors.black : Colors.white,
17 | letterSpacing: 0.15,
18 | ),
19 | );
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/packages/creamy_field/lib/src/syntax_highlighter.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/painting.dart';
3 | import 'syntax_highlighter/creamy_syntax_highlighter.dart'
4 | show CreamySyntaxHighlighter;
5 | import 'syntax_highlighter/dummy_syntax_highlighter.dart'
6 | show DummySyntaxHighlighter;
7 |
8 | /// This is the interface that must be implemented by a Syntax highlighter
9 | /// like [CreamySyntaxHighlighter] & [DummySyntaxHighlighter]
10 | abstract class SyntaxHighlighter {
11 | /// Parses text from [value] & generates syntax highlighted text as list of [TextSpan].
12 | List parseTextEditingValue(TextEditingValue? value);
13 | }
14 |
--------------------------------------------------------------------------------
/tools/status.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | MAIN_FLUTTER_APP="org.purplegraphite.code"
3 |
4 |
5 | MAIN_PUBSPEC="$MAIN_FLUTTER_APP/pubspec.yaml"
6 | CURRENT_VERSION="$(sed -n -e 's/^.*version: //p' ${MAIN_PUBSPEC})"
7 |
8 | echo "Current version: v$CURRENT_VERSION"
9 | echo "Latest release: $(git describe --abbrev=0)"
10 | echo
11 | printf "Current contributors: \n"
12 | contributors=$(git shortlog -sne --all)
13 | echo "$contributors"
14 | echo
15 | echo "Flutter project size: $(du -sh ./$MAIN_FLUTTER_APP)"
16 | git gc --quiet
17 | git_object_count=$(git count-objects -vH)
18 | SIZE_PACK=$(sed -n -e 's/^.*size-pack: //p' <<< "$git_object_count")
19 | echo "Repository size: $SIZE_PACK (exact disk space consumed)"
--------------------------------------------------------------------------------
/org.purplegraphite.code/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.3.50'
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:4.0.1'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | jcenter()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/tools/support/snr.py:
--------------------------------------------------------------------------------
1 | """Helper python script to do "inline search & replace" for update_version.sh"""
2 |
3 | from tempfile import mkstemp
4 | from shutil import move, copymode
5 | from os import fdopen, remove
6 | from sys import argv
7 |
8 | def replace(pattern, subst, file_path):
9 | #Create temp file
10 | fh, abs_path = mkstemp()
11 | with fdopen(fh,'w') as new_file:
12 | with open(file_path) as old_file:
13 | for line in old_file:
14 | new_file.write(line.replace(pattern, subst))
15 | #Copy the file permissions from the old file to the new file
16 | copymode(file_path, abs_path)
17 | #Remove original file
18 | remove(file_path)
19 | #Move new file
20 | move(abs_path, file_path)
21 |
22 | replace(argv[1], argv[2], argv[3])
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/utils/checksum.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 | import 'package:crypto/crypto.dart';
3 |
4 | /// This can be used to compute a sha256 hash checksum function on bytes & files.
5 | class Checksum {
6 | /// This class must not have an instance
7 | Checksum._();
8 |
9 | static String getBytesDigest(List bytes) {
10 | Digest digest = sha256.convert(bytes);
11 | return digest.toString();
12 | }
13 |
14 | /// Asynchronously returns file's checksum digest as String
15 | static Future getDigest(File file) async {
16 | return getBytesDigest(await file.readAsBytes());
17 | }
18 |
19 | /// Synchronously returns file's checksum digest as String
20 | static String getDigestSync(File file) {
21 | return getBytesDigest(file.readAsBytesSync());
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | *.jks
7 | .DS_Store
8 | .atom/
9 | .buildlog/
10 | .history
11 | .svn/
12 | output
13 |
14 | # IntelliJ related
15 | *.iml
16 | *.ipr
17 | *.iws
18 | .idea/
19 |
20 | # The .vscode folder contains launch configuration and tasks you configure in
21 | # VS Code which you may wish to be included in version control, so this line
22 | # is commented out by default.
23 | #.vscode/
24 |
25 | # Flutter/Dart/Pub related
26 | **/doc/api/
27 | .dart_tool/
28 | .flutter-plugins
29 | .flutter-plugins-dependencies
30 | .packages
31 | .pub-cache/
32 | .pub/
33 | pubspec.lock
34 | /build/
35 |
36 | # Web related
37 | lib/generated_plugin_registrant.dart
38 |
39 | # Symbolication related
40 | app.*.symbols
41 |
42 | # Obfuscation related
43 | app.*.map.json
44 |
45 | # Exceptions to above rules.
46 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
47 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/common/routing_const.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart' show ValueKey;
2 |
3 | const String StartScreenRoute = '/start';
4 |
5 | /// Update settings with `Provider.of(context, listen: false).updateSettings`
6 | /// before pushing this route
7 | const String EditorScreenRoute = '/editor';
8 | const String WorkspaceExplorerScreenRoute =
9 | '$EditorScreenRoute/workspace_explorer';
10 | const String BrowserScreenRoute = '/browser';
11 | const String HistoryScreenRoute = '/history';
12 | const String TerminalScreenRoute = '$EditorScreenRoute/terminal';
13 | const String SettingsScreenRoute = '/settings';
14 | const String RootRoute = '/';
15 |
16 | // Here, the value of this key below is compared when widgets are refreshed. If the value matches
17 | // with an existing key in the widget tree, then the widget updates instead of remounting.
18 | const ValueKey RootRouteKey = const ValueKey('rootScreen');
19 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/ui/components/about/version.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class Version extends StatelessWidget {
4 | final String version;
5 | const Version({Key key, this.version}) : super(key: key);
6 | @override
7 | Widget build(BuildContext context) {
8 | final Color textColor = (Theme.of(context).brightness == Brightness.dark)
9 | ? Colors.white
10 | : Colors.black;
11 | return RichText(
12 | textAlign: TextAlign.center,
13 | text: TextSpan(
14 | text: 'version ',
15 | style: Theme.of(context).textTheme.bodyText2.copyWith(
16 | fontWeight: FontWeight.bold,
17 | fontStyle: FontStyle.italic,
18 | color: Colors.green),
19 | children: [
20 | TextSpan(
21 | text: version,
22 | style: Theme.of(context)
23 | .textTheme
24 | .bodyText2
25 | .copyWith(fontStyle: FontStyle.italic, color: textColor),
26 | ),
27 | ],
28 | ),
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/service/saver.dart:
--------------------------------------------------------------------------------
1 | import 'package:code/src/ui/screens/editor/controller.dart';
2 |
3 | class FileSaving {
4 | static List pages = [];
5 |
6 | void add(EditorTabPage page) {
7 | pages.add(page);
8 | _addOnChangeSaveCallback(page);
9 | }
10 |
11 | void remove(EditorTabPage page) {
12 | pages.remove(page);
13 | }
14 |
15 | bool _isAutoSaveEnabled = true;
16 |
17 | void _addOnChangeSaveCallback(EditorTabPage page) {
18 | if (page.textEditingController != null) {
19 | page.textEditingController.addListener(() async {
20 | if (_isAutoSaveEnabled) {
21 | await save(page);
22 | }
23 | });
24 | }
25 | }
26 |
27 | Future save(EditorTabPage page) async {
28 | if (page.textEditingController != null) {
29 | final exists = await page.fileEntity.exists();
30 | if (!exists) return;
31 | await page.fileEntity.writeAsString(page.textEditingController.text);
32 | }
33 | }
34 |
35 | void enableAutoSave() {
36 | _isAutoSaveEnabled = true;
37 | }
38 |
39 | void disableAutoSave() {
40 | _isAutoSaveEnabled = false;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/packages/creamy_field/lib/creamy_field.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020, Mushaheed Syed. All rights reserved.
2 | //
3 | // Use of this source code is governed by a BSD 3-Clause License that can be
4 | // found in the LICENSE file.
5 |
6 | /// This library provides rich text editing field, controllers, syntax highlighting classes, etc.
7 |
8 | // text controller
9 | export 'src/text_tools/creamy_editing_controller.dart';
10 |
11 | // text tools
12 | export './src/text_tools/text_selection.dart';
13 | export './src/text_tools/toolbar_options.dart';
14 | export './src/text_tools/extensions.dart';
15 |
16 | // decoration
17 | export './src/decoration/line_indicator.dart'
18 | show LineCountIndicatorDecoration, LineCountIndicator;
19 | export './src/decoration/horizontal_scrollable.dart' show HorizontalScrollable;
20 |
21 | // syntax highlighter
22 | export './src/syntax_highlighter/creamy_syntax_highlighter.dart';
23 | export './src/syntax_highlighter/language_type.dart';
24 | export './src/syntax_highlighter/highlighted_theme_type.dart';
25 | export './src/syntax_highlighter.dart';
26 |
27 | // Don't keep in releases, ONLY FOR TEST PURPOSES
28 | // export './src/syntax_highlighter/dummy_syntax_highlighter.dart';
29 |
--------------------------------------------------------------------------------
/packages/creamy_field/example/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:example/main.dart';
12 |
13 | void main() {
14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(MyEditorApp());
17 |
18 | // Verify that our counter starts at 0.
19 | expect(find.text('0'), findsOneWidget);
20 | expect(find.text('1'), findsNothing);
21 |
22 | // Tap the '+' icon and trigger a frame.
23 | await tester.tap(find.byIcon(Icons.add));
24 | await tester.pump();
25 |
26 | // Verify that our counter has incremented.
27 | expect(find.text('0'), findsNothing);
28 | expect(find.text('1'), findsOneWidget);
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/packages/creamy_field/example/README.md:
--------------------------------------------------------------------------------
1 | # example
2 |
3 | This example demonstrates a sample usage of `creamy_field` package.
4 |
5 | ## Usage with TextEditingController & Syntax highlighter
6 |
7 | The below example shows the [CreamyEditingController] as [TextEditingController]
8 | which uses CreamySyntaxHighlighter for highlighting syntax of text value from
9 | the text field which uses this controller.
10 |
11 | ```dart
12 | TextEditingController controller = CreamyEditingController(
13 | syntaxHighlighter: CreamySyntaxHighlighter(
14 | language: LanguageType.dart,
15 | theme: HighlightedThemeType.githubTheme,
16 | ),
17 | );
18 | ```
19 |
20 | ## CreamyField
21 |
22 | A CreamyField is a text field which supports CreamyEditingController & CreamySyntaxHighlighter.
23 | This text field is similar to Material TextField but will be tailored (expected in future) for
24 | writing rich text, especially for markup & programming languages.
25 |
26 | For now, use it like Material TextField as
27 |
28 | ```dart
29 | CreamyField(
30 | autofocus: true,
31 | controller: controller,
32 | textCapitalization: TextCapitalization.none,
33 | decoration: InputDecoration.collapsed(hintText: 'Start writing'),
34 | );
35 | ```
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 |
5 | ---
6 |
7 |
16 |
17 | ## Summary
18 |
19 |
20 |
21 | ## Motivation
22 |
23 |
24 |
25 | ## Describe alternatives you've considered
26 |
27 |
28 |
29 | ## Additional context
30 |
31 |
32 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/models/hive/settings/generalSettings.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'generalSettings.dart';
4 |
5 | // **************************************************************************
6 | // TypeAdapterGenerator
7 | // **************************************************************************
8 |
9 | class GeneralSettingsAdapter extends TypeAdapter {
10 | @override
11 | final int typeId = 1;
12 |
13 | @override
14 | GeneralSettings read(BinaryReader reader) {
15 | final numOfFields = reader.readByte();
16 | final fields = {
17 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
18 | };
19 | return GeneralSettings().._debuggingEnabled = fields[1] as bool;
20 | }
21 |
22 | @override
23 | void write(BinaryWriter writer, GeneralSettings obj) {
24 | writer
25 | ..writeByte(1)
26 | ..writeByte(1)
27 | ..write(obj._debuggingEnabled);
28 | }
29 |
30 | @override
31 | int get hashCode => typeId.hashCode;
32 |
33 | @override
34 | bool operator ==(Object other) =>
35 | identical(this, other) ||
36 | other is GeneralSettingsAdapter &&
37 | runtimeType == other.runtimeType &&
38 | typeId == other.typeId;
39 | }
40 |
--------------------------------------------------------------------------------
/packages/creamy_field/lib/src/syntax_highlighter/dummy_syntax_highlighter.dart:
--------------------------------------------------------------------------------
1 | import 'dart:ui';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:creamy_field/creamy_field.dart';
5 |
6 | /// This is a dummy implementation of Syntax highlighter for Testing purpose.
7 | /// Ideally, you would implement the `SyntaxHighlighterBase` interface as per your need of highlighting rules.
8 | class DummySyntaxHighlighter implements SyntaxHighlighter {
9 | @override
10 | List parseTextEditingValue(TextEditingValue? tev) {
11 | var texts = tev!.text.split(' ');
12 |
13 | var lsSpans = [];
14 | texts.forEach((text) {
15 | if (text == 'class') {
16 | lsSpans
17 | .add(TextSpan(text: text, style: TextStyle(color: Colors.green)));
18 | } else if (text == 'if' || text == 'else') {
19 | lsSpans.add(TextSpan(text: text, style: TextStyle(color: Colors.blue)));
20 | } else if (text == 'return') {
21 | lsSpans.add(TextSpan(text: text, style: TextStyle(color: Colors.red)));
22 | } else {
23 | lsSpans
24 | .add(TextSpan(text: text, style: TextStyle(color: Colors.black)));
25 | }
26 | lsSpans.add(TextSpan(text: ' ', style: TextStyle(color: Colors.black)));
27 | });
28 | return lsSpans;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/utils/fileutils.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 |
4 | import 'package:code/src/models/plain_model/entity.dart';
5 | import 'package:code/src/utils/permissions.dart';
6 |
7 | class FileUtils {
8 | static const String _emulatedSdcardRoot = '/storage/emulated/0';
9 |
10 | static Uri _sdcardRootURI = Uri.file(_emulatedSdcardRoot);
11 |
12 | static Directory primaryRoot = Directory.fromUri(_sdcardRootURI);
13 |
14 | static Future> _dirContents(Directory dir) {
15 | var files = [];
16 | var completer = new Completer>();
17 | var lister = dir.list(recursive: false);
18 | lister.listen(
19 | (file) async {
20 | var x = Entity(file);
21 | await x.updateStatus();
22 | files.add(x);
23 | },
24 | // should also register onError
25 | onDone: () => completer.complete(files),
26 | );
27 | return completer.future;
28 | }
29 |
30 | static Future createDirectory(Directory directory) async {
31 | return await directory.create(recursive: true);
32 | }
33 |
34 | static Future> listEntities(Directory path) async {
35 | var _c = await _dirContents(path);
36 | _c.sort();
37 | return _c;
38 | }
39 |
40 | static void checkPerms() async {
41 | await Perms.ask();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/models/hive/settings/themeSettings.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'themeSettings.dart';
4 |
5 | // **************************************************************************
6 | // TypeAdapterGenerator
7 | // **************************************************************************
8 |
9 | class ThemeSettingsAdapter extends TypeAdapter {
10 | @override
11 | final int typeId = 2;
12 |
13 | @override
14 | ThemeSettings read(BinaryReader reader) {
15 | final numOfFields = reader.readByte();
16 | final fields = {
17 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
18 | };
19 | return ThemeSettings(
20 | fields[1] as int,
21 | fields[2] as String,
22 | );
23 | }
24 |
25 | @override
26 | void write(BinaryWriter writer, ThemeSettings obj) {
27 | writer
28 | ..writeByte(2)
29 | ..writeByte(1)
30 | ..write(obj.themeChoice)
31 | ..writeByte(2)
32 | ..write(obj.themeModeS);
33 | }
34 |
35 | @override
36 | int get hashCode => typeId.hashCode;
37 |
38 | @override
39 | bool operator ==(Object other) =>
40 | identical(this, other) ||
41 | other is ThemeSettingsAdapter &&
42 | runtimeType == other.runtimeType &&
43 | typeId == other.typeId;
44 | }
45 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/models/hive/settings/themeSettings.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart' show ThemeMode;
2 | import 'package:hive/hive.dart';
3 |
4 | part 'themeSettings.g.dart';
5 |
6 | /// Hive Model which has application theme related information
7 | @HiveType(typeId: 2)
8 | class ThemeSettings extends HiveObject {
9 | /// User's choice of theme. defaults to 0. Updated later with User's preference.
10 | @HiveField(1)
11 | int themeChoice;
12 |
13 | /// String representation of [ThemeMode].
14 | @HiveField(2)
15 | String themeModeS;
16 |
17 | /// Returns ThemeMode.
18 | ThemeMode get themeMode {
19 | return _from(themeModeS);
20 | }
21 |
22 | /// Set Application ThemeMode.
23 | void set themeMode(ThemeMode themeMode) {
24 | themeModeS = themeMode.toString();
25 | }
26 |
27 | /// TO BE USED WITH GENERATOR. Use [ThemeSettings.manual] instead.
28 | ThemeSettings(this.themeChoice, this.themeModeS);
29 |
30 | ThemeSettings.manual(this.themeChoice, ThemeMode mode) {
31 | themeMode = mode;
32 | }
33 |
34 | ThemeMode _from(String mode) {
35 | switch (mode) {
36 | case "ThemeMode.system":
37 | return ThemeMode.system;
38 | case "ThemeMode.light":
39 | return ThemeMode.light;
40 | case "ThemeMode.dark":
41 | return ThemeMode.dark;
42 | default:
43 | throw Exception();
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/ui/components/popup_menu_tile.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class PopupMenuTile extends PopupMenuEntry {
4 | final String value;
5 | final IconData leading;
6 | final Widget title;
7 | final Color color;
8 | final void Function() onTap;
9 |
10 | const PopupMenuTile({
11 | Key key,
12 | this.value,
13 | this.leading,
14 | this.title,
15 | this.onTap,
16 | this.color,
17 | }) : super(key: key);
18 |
19 | @override
20 | _PopupMenuTileState createState() => _PopupMenuTileState();
21 |
22 | @override
23 | // TODO: implement height
24 | double get height => throw UnimplementedError();
25 |
26 | @override
27 | bool represents(dynamic value) {
28 | return true;
29 | }
30 | }
31 |
32 | class _PopupMenuTileState extends State {
33 | @override
34 | Widget build(BuildContext context) {
35 | return PopupMenuItem(
36 | key: ValueKey(widget.value),
37 | value: widget.value,
38 | child: Row(
39 | mainAxisSize: MainAxisSize.min,
40 | children: [
41 | Padding(
42 | padding: EdgeInsets.only(right: 8, left: 8),
43 | child: Icon(
44 | widget.leading,
45 | color: widget.color,
46 | ),
47 | ),
48 | widget.title,
49 | ],
50 | ),
51 | );
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/packages/creamy_field/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: example
2 | description: A new Flutter project.
3 |
4 | # The following defines the version and build number for your application.
5 | # A version number is three numbers separated by dots, like 1.2.43
6 | # followed by an optional build number separated by a +.
7 | # Both the version and the builder number may be overridden in flutter
8 | # build by specifying --build-name and --build-number, respectively.
9 | # In Android, build-name is used as versionName while build-number used as versionCode.
10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
12 | # Read more about iOS versioning at
13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
14 | version: 1.0.0+1
15 |
16 | publish_to: none
17 |
18 | environment:
19 | sdk: '>=2.12.0 <3.0.0'
20 |
21 | dependencies:
22 | flutter:
23 | sdk: flutter
24 |
25 | # The following adds the Cupertino Icons font to your application.
26 | # Use with the CupertinoIcons class for iOS style icons.
27 | cupertino_icons: ^1.0.2
28 | creamy_field:
29 | path: ../
30 |
31 | dev_dependencies:
32 | flutter_test:
33 | sdk: flutter
34 | flutter_lints: ^1.0.0
35 |
36 | flutter:
37 | uses-material-design: true
38 | # dependency_overrides:
39 | # creamy_field:
40 | # path: ../
41 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 |
5 | ---
6 |
7 |
14 |
15 | ### Prerequisites
16 |
17 | * [ ] Put an X between the brackets on this line if you have done all of the following:
18 | * Reproduced the problem in Release & Debug Mode
19 | * Checked that your issue isn't already filed:
20 |
21 | ### Description
22 |
23 |
24 |
25 | ### Steps to Reproduce
26 |
27 | 1.
28 | 2.
29 | 3.
30 |
31 | **Expected behavior:**
32 |
33 |
34 |
35 | **Actual behavior:**
36 |
37 |
38 |
39 | **Reproduces how often:**
40 |
41 |
42 |
43 | ### Versions
44 |
45 |
46 |
47 | ### Additional Information
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/ui/components/acrylic.dart:
--------------------------------------------------------------------------------
1 | import 'dart:ui';
2 |
3 | import 'package:flutter/material.dart' show Colors, Theme;
4 | import 'package:flutter/widgets.dart';
5 |
6 | /// Adds acrylic glass-like effect to child.
7 | class Acrylic extends StatelessWidget {
8 | /// Acrylic effect is only applied to this child
9 | final Widget child;
10 | final bool enabled;
11 | final bool isDark;
12 | final double blurIntensity;
13 |
14 | /// Changes theme's canvas color
15 | final Widget Function(BuildContext, Widget) builder;
16 | const Acrylic({
17 | @required this.child,
18 | Key key,
19 | this.builder,
20 | this.enabled = true,
21 | this.isDark,
22 | this.blurIntensity = 5.0,
23 | }) : assert(child != null, 'child should not be null'),
24 | super(key: key);
25 | @override
26 | Widget build(BuildContext context) {
27 | if (!enabled) return child;
28 | final Widget acrylicEnabledchild = BackdropFilter(
29 | filter: ImageFilter.blur(sigmaX: blurIntensity, sigmaY: blurIntensity),
30 | child: Container(
31 | decoration: BoxDecoration(
32 | color: Colors.grey.withOpacity(isDark ? 0.25 : 0.5),
33 | ),
34 | child: child,
35 | ),
36 | );
37 | if (builder != null) {
38 | return Theme(
39 | data: Theme.of(context).copyWith(
40 | canvasColor: Theme.of(context).canvasColor.withAlpha(0x44),
41 | ),
42 | child: builder(context, acrylicEnabledchild),
43 | );
44 | }
45 | return acrylicEnabledchild;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2020, Mushaheed Syed
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | 3. Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/packages/creamy_field/example/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | linter:
13 | # The lint rules applied to this project can be customized in the
14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
15 | # included above or to enable additional rules. A list of all available lints
16 | # and their documentation is published at
17 | # https://dart-lang.github.io/linter/lints/index.html.
18 | #
19 | # Instead of disabling a lint rule for the entire project in the
20 | # section below, it can also be suppressed for a single line of code
21 | # or a specific dart file by using the `// ignore: name_of_lint` and
22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
23 | # producing the lint.
24 | rules:
25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
27 |
28 | # Additional information about this file can be found at
29 | # https://dart.dev/guides/language/analysis-options
30 |
--------------------------------------------------------------------------------
/packages/creamy_field/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2020, Mushaheed Syed
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | 3. Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
/packages/creamy_field/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 0.4.1
4 |
5 | - Make compatible with flutter v2.8
6 |
7 | ## 0.4.0
8 |
9 | - Modified style-usage of public APIs
10 | - Migrate to null-safety
11 | - Removed CreamyField & the classes it depended
12 | - Removed onEnterPress & onBackspacePress method head from SyntaxHighlighter
13 |
14 | ## 0.3.3
15 |
16 | - Made compatible with flutter 1.22
17 | - Removed onEnterPress & onBackspacePress callbacks in CreamyField
18 | - Removed vertical padding from the Text field
19 |
20 | ## 0.3.2
21 |
22 | - Flutter 1.20 capability fix
23 | - Ability to change syntax highlighting based on system ThemeMode & app Brightness
24 |
25 | ## 0.3.1
26 |
27 | - tabSpace can be set in CreamyEditingController
28 | - removed tabSpace from CreamySyntaxHighlighter
29 | - property to add tab to text at baseOffset
30 |
31 | ## 0.3.0+hotfix.2
32 |
33 | - renamed RichEditableText
34 | - added a few tests, expecting more
35 | - changed text description getters. (could be breaking)
36 |
37 | ## 0.3.0+hotfix.1
38 |
39 | - fixed documentation issues
40 | - fixed null exception issues
41 | - added asserts
42 |
43 | ## 0.3.0
44 |
45 | - fix delayed animation of line indicator column
46 | - Custom text selection controls
47 | - extended text selection widget
48 |
49 | ## 0.2.2
50 |
51 | - line indicator in creamy text field
52 | - text field horizontally scrollable
53 |
54 | ## 0.2.1
55 |
56 | - Documentation improvements
57 |
58 | ## 0.2.0
59 |
60 | - Modified Text controller
61 | - Modified new syntax highligher as an implementation of `SyntaxHighlighter` and renamed it with prefix Creamy
62 |
63 | ## 0.1.0
64 |
65 | - CreamyField to be used as a Rich Editing Text Field
66 | - Syntax highlighting support
67 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/utils/permissions.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:flutter/material.dart' show WidgetsFlutterBinding;
4 | import 'package:flutter/services.dart';
5 | import 'package:permission_handler/permission_handler.dart';
6 |
7 | class Perms {
8 | static Map requestedResult;
9 | static final Permission _storageAccessPerm = Permission.storage;
10 |
11 | // TODO: make a dialog to describe why we need permission
12 | /// Requests permissions only once. Strictly requires user to accept, on denial will exit the app.
13 | static Future askOnce() async {
14 | WidgetsFlutterBinding.ensureInitialized();
15 | var status = await _storageAccessPerm.status;
16 | if (!status.isGranted) {
17 | // We didn't have permissions yet.
18 | await ask();
19 | }
20 | // async completed
21 | return true;
22 | }
23 |
24 | static Future ask() async {
25 | //Requesting multiple permissions at once.
26 | requestedResult = await [_storageAccessPerm].request();
27 | // Iterating map to check permissions
28 | requestedResult.forEach((perm, permStatus) async {
29 | if (await perm.request().isGranted) {
30 | // Either the permission was already granted before or the user just granted it.
31 | } else {
32 | // Not granted, so opening settings
33 | openAppSettings();
34 | }
35 | await recheck(perm);
36 | });
37 | }
38 |
39 | static Future recheck(Permission perm) async {
40 | // Re-checking & Re-requesting
41 | if (!(await perm.request().isGranted)) {
42 | // Exit App
43 | await SystemNavigator.pop(animated: true);
44 | exit(1);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/packages/creamy_field/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | .dart_tool/
26 | .flutter-plugins
27 | .packages
28 | .pub-cache/
29 | .pub/
30 | /build/
31 |
32 | # Android related
33 | **/android/**/gradle-wrapper.jar
34 | **/android/.gradle
35 | **/android/captures/
36 | **/android/gradlew
37 | **/android/gradlew.bat
38 | **/android/local.properties
39 | **/android/**/GeneratedPluginRegistrant.java
40 |
41 | # iOS/XCode related
42 | **/ios/**/*.mode1v3
43 | **/ios/**/*.mode2v3
44 | **/ios/**/*.moved-aside
45 | **/ios/**/*.pbxuser
46 | **/ios/**/*.perspectivev3
47 | **/ios/**/*sync/
48 | **/ios/**/.sconsign.dblite
49 | **/ios/**/.tags*
50 | **/ios/**/.vagrant/
51 | **/ios/**/DerivedData/
52 | **/ios/**/Icon?
53 | **/ios/**/Pods/
54 | **/ios/**/.symlinks/
55 | **/ios/**/profile
56 | **/ios/**/xcuserdata
57 | **/ios/.generated/
58 | **/ios/Flutter/App.framework
59 | **/ios/Flutter/Flutter.framework
60 | **/ios/Flutter/Generated.xcconfig
61 | **/ios/Flutter/app.flx
62 | **/ios/Flutter/app.zip
63 | **/ios/Flutter/flutter_assets/
64 | **/ios/ServiceDefinitions.json
65 | **/ios/Runner/GeneratedPluginRegistrant.*
66 |
67 | # Exceptions to above rules.
68 | !**/ios/**/default.mode1v3
69 | !**/ios/**/default.mode2v3
70 | !**/ios/**/default.pbxuser
71 | !**/ios/**/default.perspectivev3
72 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
73 |
74 | android/
75 | ios/
76 | web/
--------------------------------------------------------------------------------
/.idea/code.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/packages/creamy_field/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | .dart_tool/
26 | .flutter-plugins
27 | .packages
28 | .pub-cache/
29 | .pub/
30 | build/
31 | doc/
32 |
33 | # Android related
34 | **/android/**/gradle-wrapper.jar
35 | **/android/.gradle
36 | **/android/captures/
37 | **/android/gradlew
38 | **/android/gradlew.bat
39 | **/android/local.properties
40 | **/android/**/GeneratedPluginRegistrant.java
41 |
42 | # iOS/XCode related
43 | **/ios/**/*.mode1v3
44 | **/ios/**/*.mode2v3
45 | **/ios/**/*.moved-aside
46 | **/ios/**/*.pbxuser
47 | **/ios/**/*.perspectivev3
48 | **/ios/**/*sync/
49 | **/ios/**/.sconsign.dblite
50 | **/ios/**/.tags*
51 | **/ios/**/.vagrant/
52 | **/ios/**/DerivedData/
53 | **/ios/**/Icon?
54 | **/ios/**/Pods/
55 | **/ios/**/.symlinks/
56 | **/ios/**/profile
57 | **/ios/**/xcuserdata
58 | **/ios/.generated/
59 | **/ios/Flutter/App.framework
60 | **/ios/Flutter/Flutter.framework
61 | **/ios/Flutter/Generated.xcconfig
62 | **/ios/Flutter/app.flx
63 | **/ios/Flutter/app.zip
64 | **/ios/Flutter/flutter_assets/
65 | **/ios/ServiceDefinitions.json
66 | **/ios/Runner/GeneratedPluginRegistrant.*
67 |
68 | # Exceptions to above rules.
69 | !**/ios/**/default.mode1v3
70 | !**/ios/**/default.mode2v3
71 | !**/ios/**/default.pbxuser
72 | !**/ios/**/default.perspectivev3
73 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
74 | example/android/
75 | .vscode/
76 |
77 | example/ios/Flutter/flutter_export_environment.sh
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/models/hive/history.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:hive/hive.dart';
3 |
4 | part 'history.g.dart';
5 |
6 | /// UNIMPLEMENTED for a list of open files
7 | @HiveType(typeId: 6)
8 | class FileModificationHistory extends HiveObject {
9 | @HiveField(1)
10 | final String absolutePath;
11 | @HiveField(2)
12 | DateTime _lastModified;
13 |
14 | DateTime get lastModified => _lastModified;
15 |
16 | void updateLastModified() {
17 | _lastModified = DateTime.now();
18 | }
19 |
20 | @HiveField(3)
21 | final double scrollOffset;
22 | @HiveField(4)
23 | final int cursorOffset;
24 |
25 | FileModificationHistory({
26 | this.absolutePath,
27 | this.scrollOffset,
28 | this.cursorOffset,
29 | });
30 |
31 | void setlatestModified() {
32 | _lastModified = DateTime.now();
33 | }
34 | }
35 |
36 | @HiveType(typeId: 5)
37 | class History extends HiveObject with Comparable {
38 | @HiveField(1)
39 | final String workspacePath;
40 |
41 | @HiveField(2)
42 | FileModificationHistory lastModifiedFileDetails;
43 |
44 | @HiveField(3)
45 | DateTime _lastModified;
46 |
47 | DateTime get lastModified => _lastModified;
48 |
49 | History({@required this.workspacePath});
50 |
51 | void updateLastModifiedDateTime() {
52 | _lastModified = DateTime.now();
53 | }
54 |
55 | @override
56 | Future save() async {
57 | updateLastModifiedDateTime();
58 | if (lastModifiedFileDetails != null) await lastModifiedFileDetails.save();
59 | return await super.save();
60 | }
61 |
62 | static bool compareByDate = true;
63 |
64 | @override
65 | int compareTo(History other) {
66 | if (compareByDate ?? true) {
67 | return this.lastModified.compareTo(other.lastModified);
68 | } else {
69 | return this.workspacePath.compareTo(other.workspacePath);
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/models/hive/repository.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:hive_flutter/hive_flutter.dart';
4 |
5 | /// A repository which holds a Hive [Box] with helper functions.
6 | class Repository {
7 | final TypeAdapter adapter;
8 | final Box box;
9 | final String boxName;
10 |
11 | M get first => box.values.first;
12 |
13 | Iterable iterable() => box.values;
14 |
15 | bool get isRepositoryEmpty => box?.isEmpty ?? true;
16 |
17 | /// Creates a [Repository] to wrap an open [box] of [adapter] with name [boxName]
18 | Repository(this.boxName, this.adapter, this.box);
19 |
20 | static bool _isHiveInitialized = false;
21 |
22 | /// Initiates Hive for flutter and returns a Hive [Box] wrapped with [Repository].
23 | ///
24 | /// Registers M's [adapter] and opens [M] box of name [boxName]. (Creates if doesn't exist)
25 | ///
26 | /// Either register Type adapter with [register] or provide the TypeAdapter as the parameter in [adapter]
27 | static Future> get(String boxName,
28 | [TypeAdapter adapter]) async {
29 | if (!_isHiveInitialized) {
30 | await Hive.initFlutter();
31 | _isHiveInitialized = true;
32 | }
33 |
34 | register(adapter);
35 | final Box box = await Hive.openBox(boxName);
36 | return Repository(boxName, adapter, box);
37 | }
38 |
39 | static void register(TypeAdapter adapter) {
40 | final _isRegistered = Hive.isAdapterRegistered(adapter.typeId);
41 | if (_isRegistered) return;
42 | Hive.registerAdapter(adapter);
43 | }
44 |
45 | /// Check if box is open
46 | bool isBoxOpen() {
47 | return box?.isOpen ?? false;
48 | }
49 |
50 | /// Subscribe to Stream of BoxEvent which is triggered when a read/write
51 | /// operation is performed on [box]
52 | StreamSubscription listenStream(void Function(BoxEvent) onData) {
53 | return this.box.watch().listen(onData);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/models/plain_model/entity.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 | import 'package:path/path.dart' as path;
3 |
4 | /// A FileSystemEntity wrapper
5 | class Entity extends Comparable {
6 | String get id => absolutePath;
7 | String get name => basename;
8 | String basename;
9 | String absolutePath;
10 | FileSystemEntity entity;
11 | FileStat stat;
12 |
13 | /// Scroll Offset, initially 0
14 | double scrollOffset = 0.0;
15 |
16 | Entity(this.entity) {
17 | if (!this.entity.isAbsolute) {
18 | this.entity = this.entity.absolute;
19 | }
20 | absolutePath = this.entity.path;
21 | basename = path.basename(this.entity.path);
22 | }
23 |
24 | Entity.blank()
25 | : absolutePath = DateTime.now().millisecondsSinceEpoch.toString(),
26 | basename = 'untitled';
27 |
28 | void updateStatus() async {
29 | this.stat = await entity.stat();
30 | }
31 |
32 | @override
33 | bool operator ==(Object other) {
34 | if (other is Entity) {
35 | return this.absolutePath == other.absolutePath;
36 | } else
37 | return false;
38 | }
39 |
40 | static String _toLowercaseFirstChar(String string) {
41 | return "${string[0].toLowerCase()}${string.substring(1)}";
42 | }
43 |
44 | @override
45 | int compareTo(Entity other) {
46 | // Converting 1st char to lowercase
47 | String a = _toLowercaseFirstChar(this.basename);
48 | String b = _toLowercaseFirstChar(other.basename);
49 |
50 | if (a[0] == '.' || b[0] == '.') {
51 | if (a[0] == '.' && b[0] == '.') {
52 | a = _toLowercaseFirstChar(a.substring(1));
53 | b = _toLowercaseFirstChar(b.substring(1));
54 | } else if (a[0] == '.') {
55 | return 1;
56 | } else if (b[0] == '.') {
57 | return -1;
58 | }
59 | }
60 |
61 | if (a == b) return 0;
62 | var _c = [a, b];
63 | _c.sort();
64 | if (_c[0] == a) {
65 | return -1;
66 | } else {
67 | return 1;
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/packages/creamy_field/lib/src/text_tools/extensions.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/widgets.dart' show TextEditingController;
2 |
3 | /// Extension methods on [TextEditingController] for additional information about the editing text.
4 |
5 | extension CreamyTextFieldExtensions on TextEditingController {
6 | /// The text currently under selection
7 | String get selectedText => selection.textInside(text);
8 |
9 | /// The text before the current text selection
10 | String get beforeSelectedText => selection.textBefore(text);
11 |
12 | /// The text after the current text selection
13 | String get afterSelectedText => selection.textAfter(text);
14 |
15 | /// Total number of lines in the [text]
16 | int get totalLineCount => text.split('\n').length;
17 |
18 | /// The line at which end cursor lies
19 | int get atLine => beforeSelectedText.split('\n').length;
20 |
21 | /// The column at which the end cursor is at.
22 | int get atColumn {
23 | int _extent = selection.extentOffset;
24 | String precursorText = text.substring(0, _extent);
25 | return (_extent - precursorText.lastIndexOf('\n'));
26 | }
27 |
28 | /// The column index where the selection extent ends.
29 | /// Same as [atColumn].
30 | int get extentColumn {
31 | return atColumn;
32 | }
33 |
34 | /// The column index at which the selection (base) begins.
35 | int get baseColumn {
36 | int _base = selection.baseOffset;
37 | String precursorText = text.substring(0, _base);
38 | return (_base - precursorText.lastIndexOf('\n'));
39 | }
40 |
41 | // TODO: add extensions to TextEditingValue
42 | Map get textDescriptionMap => {
43 | 'text': text,
44 | 'beforeSelectedText': beforeSelectedText,
45 | 'afterSelectedText': afterSelectedText,
46 | 'selectedText': selectedText,
47 | 'totalLines': totalLineCount,
48 | 'atLine': atLine,
49 | 'atColumn': atColumn,
50 | 'baseColumn': baseColumn,
51 | 'extentColumn': extentColumn,
52 | };
53 | }
54 |
--------------------------------------------------------------------------------
/.github/workflows/master.yml:
--------------------------------------------------------------------------------
1 | # This is a basic workflow to help you get started with Actions
2 |
3 | name: Flutter build test
4 |
5 | # Controls when the action will run. Triggers the workflow on push or pull request
6 | # events but only for the master branch
7 | on:
8 | push:
9 | branches: [master]
10 | pull_request:
11 | branches: [master]
12 |
13 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel
14 | jobs:
15 | # This workflow contains a single job called "analysis"
16 | buildTests:
17 | # The type of runner that the job will run on
18 | runs-on: ubuntu-latest
19 |
20 | # Steps represent a sequence of tasks that will be executed as part of the job
21 | steps:
22 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
23 | - uses: actions/checkout@v2
24 | # Setup Java environment in order to build the Android app.
25 | - uses: actions/setup-java@v1
26 | with:
27 | java-version: "12.x"
28 | - uses: subosito/flutter-action@v1.5.3
29 | with:
30 | # The Flutter version to make available on the path
31 | flutter-version: 2.8.1 # optional
32 | # The Flutter build release channel
33 | channel: stable # optional, default is stable
34 |
35 | - name: Install NDK
36 | run: echo "y" | sudo /usr/local/lib/android/sdk/tools/bin/sdkmanager --install "ndk;21.0.6113669" --sdk_root=${ANDROID_SDK_ROOT}
37 |
38 | - name: Building debug apk
39 | run: |
40 | cd $GITHUB_WORKSPACE/org.purplegraphite.code;
41 | flutter build apk --debug --target-platform=android-arm64 --split-debug-info=output/symbols;
42 |
43 | - name: Uploading artifacts to zip
44 | uses: actions/upload-artifact@v1.0.0
45 | with:
46 | # Artifact name
47 | name: org.purplegraphite.code-debug-android_apks.zip
48 | # Directory containing files to upload
49 | path: org.purplegraphite.code/build/app/outputs/apk/debug/
50 |
--------------------------------------------------------------------------------
/.idea/libraries/Dart_SDK.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/ui/components/buttons/action_tabs_button.dart:
--------------------------------------------------------------------------------
1 | import 'package:code/src/ui/screens/editor/controller.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:provider/provider.dart';
4 |
5 | class ActionsTabButton extends StatelessWidget {
6 | /// Return proper tab button label based on number of tabs open
7 | static String changeLabel(Iterable tabsOpen) {
8 | if (tabsOpen?.isEmpty ?? true) {
9 | return '0';
10 | } else if (tabsOpen.length > 99) {
11 | return ':P';
12 | } else {
13 | return tabsOpen.length.toString();
14 | }
15 | }
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | final EditorController editorController =
20 | Provider.of(context);
21 | final ThemeData theme = Theme.of(context);
22 | final bool isDark = theme.brightness == Brightness.dark;
23 | Color appbarAccent = isDark ? Colors.amber : Colors.white;
24 | final tabs = editorController.tabs;
25 | String label = changeLabel(tabs);
26 | return Tooltip(
27 | message: 'change tabs',
28 | child: IconButton(
29 | onPressed: () {
30 | Scaffold.of(context).openEndDrawer();
31 | },
32 | icon: Center(
33 | child: Container(
34 | padding: const EdgeInsets.all(2),
35 | alignment: Alignment.center,
36 | decoration: BoxDecoration(
37 | borderRadius: BorderRadius.circular(6), // To match with chrome
38 | // borderRadius: BorderRadius.circular(25), // Original
39 | border: Border.all(
40 | width: 2, color: isDark ? appbarAccent : Colors.white),
41 | ),
42 | constraints: BoxConstraints.tight(const Size(25.0, 25.0)),
43 | child: Text(
44 | '$label',
45 | style: TextStyle(
46 | color: appbarAccent,
47 | fontWeight: isDark ? FontWeight.w800 : FontWeight.bold,
48 | fontSize: 10,
49 | ),
50 | ),
51 | ),
52 | ),
53 | ),
54 | );
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/packages/creamy_field/lib/src/decoration/horizontal_scrollable.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/gestures.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter/rendering.dart';
4 |
5 | /// Makes the child horizontally scrollable
6 | class HorizontalScrollable extends StatelessWidget {
7 | /// Scrolling horizontally would be only enabled if this value is true
8 | final bool beScrollable;
9 |
10 | final Widget? child;
11 |
12 | final ScrollController? scrollController;
13 |
14 | /// In the future, this controller from a child TextField will be used to determine horizontal scroll extent.
15 | final TextEditingController? textEditingController;
16 |
17 | final ScrollPhysics physics;
18 |
19 | /// How much this widget should get horizontally scrolled.
20 | /// Defaults to 2000
21 | // TODO: Change this static value with the calculation horizontal scroll extent based on the pixel length of text of the longest line in textField.
22 | final double horizontalScrollExtent;
23 |
24 | /// Wrap with Expanded widget
25 | final bool expand;
26 |
27 | /// Scroll padding of the text field under this scrollable
28 | /// defaults to `EdgeInsets.zero`
29 | final EdgeInsetsGeometry padding;
30 |
31 | /// Makes the child horizontally scrollable
32 | const HorizontalScrollable({
33 | Key? key,
34 | this.textEditingController,
35 | this.scrollController,
36 | this.physics = const ClampingScrollPhysics(),
37 | this.child,
38 | this.beScrollable = true,
39 | this.horizontalScrollExtent = 2000,
40 | this.padding = EdgeInsets.zero,
41 | this.expand = false,
42 | }) : assert(horizontalScrollExtent > 0,
43 | 'horizontal scroll extent should not be less than 1'),
44 | super(key: key);
45 |
46 | @override
47 | Widget build(BuildContext context) {
48 | if (!beScrollable) return child!;
49 |
50 | final _scrollable = SingleChildScrollView(
51 | padding: padding,
52 | scrollDirection: Axis.horizontal,
53 | controller: scrollController,
54 | physics: physics,
55 | dragStartBehavior: DragStartBehavior.down,
56 | child: ConstrainedBox(
57 | constraints: BoxConstraints.expand(
58 | width: horizontalScrollExtent,
59 | ),
60 | child: child,
61 | ),
62 | );
63 |
64 | if (!expand) return _scrollable;
65 |
66 | return Expanded(
67 | child: _scrollable,
68 | );
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/common/strings.dart:
--------------------------------------------------------------------------------
1 | class Strings {
2 | /// Application's version.
3 | /// Don't edit manually, use `/tools/update_version.sh`.
4 | static const String applicationVersion = '0.5.1-alpha+210410';
5 |
6 | static const String applicationTitle = 'Snake code';
7 |
8 | static var applicationLegalese = _legalese;
9 |
10 | static var aboutUs = 'about';
11 |
12 | static String contactUs = 'Contact us';
13 |
14 | static String viewLicenses = 'View licenses';
15 | }
16 |
17 | class StorageBoxNames {
18 | StorageBoxNames._();
19 |
20 | static const _base = 'org.purplegraphite.code';
21 | static const HISTORY = '$_base-history';
22 | static const THEME_SETTINGS = '$_base-themesettings';
23 | static const FILE_MODIFICATION_HISTORY = '$_base-fileModificationHistory';
24 | }
25 |
26 | const _legalese = """BSD 3-Clause License
27 |
28 | Copyright (c) 2020, Mushaheed Syed
29 | All rights reserved.
30 |
31 | Redistribution and use in source and binary forms, with or without
32 | modification, are permitted provided that the following conditions are met:
33 |
34 | 1. Redistributions of source code must retain the above copyright notice, this
35 | list of conditions and the following disclaimer.
36 |
37 | 2. Redistributions in binary form must reproduce the above copyright notice,
38 | this list of conditions and the following disclaimer in the documentation
39 | and/or other materials provided with the distribution.
40 |
41 | 3. Neither the name of the copyright holder nor the names of its
42 | contributors may be used to endorse or promote products derived from
43 | this software without specific prior written permission.
44 |
45 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
46 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
48 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
49 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
51 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
52 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
53 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
54 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.""";
55 |
--------------------------------------------------------------------------------
/tools/update_version.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | VERSION=$1
4 |
5 | # == EDIT PATHS IN THESE VARIABLES ====
6 | MAIN_PUBSPEC="org.purplegraphite.code/pubspec.yaml"
7 | CONST_STRING_DART="./org.purplegraphite.code/lib/src/commons/strings.dart"
8 | # == END ==============================
9 |
10 | LAST_VERSION="$(sed -n -e 's/^.*version: //p' ${MAIN_PUBSPEC})"
11 |
12 | echo "The previous version was: $LAST_VERSION"
13 |
14 | if [ -z "$VERSION" ]; then
15 | echo
16 | echo "No version specified."
17 | echo "Usage: tools/update_version.sh NEW_VERSION_NUMBER"
18 | exit 1
19 | fi
20 |
21 | printf "\nThis will change project's version from $LAST_VERSION to $VERSION\n"
22 | read -p "Are you sure you want to continue? (Y/n) " -n 1 -r
23 | echo
24 | if [[ $REPLY =~ ^[Nn]$ ]]
25 | then
26 | exit 0
27 | fi
28 |
29 | printf "\nUpdating pubspec versions.."
30 |
31 | # perl -pi -e "s/^(\\W*version:) $LAST_VERSION/\$1 $VERSION/g" $PUBSPECS
32 | python tools/support/snr.py "version: ${LAST_VERSION}" "version: $VERSION" $MAIN_PUBSPEC
33 | # perl -pi -e "s/^(\\W*version:) [0-9.beta-]+/\$1 $VERSION/g" $PUBSPECS
34 |
35 | printf "\nUpdating versions in app source code where required.."
36 | # Update version defined in the source code in app.
37 | # perl -pi -e "s/^(\\W*static const String applicationVersion =) '[0-9.beta-]+'/\$1 '$VERSION'/g" $CONST_STRING_DART
38 | python tools/support/snr.py "static const String applicationVersion = '${LAST_VERSION}'" "static const String applicationVersion = '$VERSION'" $CONST_STRING_DART
39 |
40 | NEW_VERSION="$(sed -n -e 's/^.*version: //p' ${MAIN_PUBSPEC})"
41 |
42 | printf "\n\nUpdated version to: $NEW_VERSION\n"
43 |
44 | # == NOT REQUIRED IN THIS PROJECT ====
45 | # # Update the version of all packages/applications.
46 |
47 | # # If you add a package that is version locked, please add it to this list as
48 | # # "./org.floraprobe/pubspec.yaml \
49 | # # ./packages/"
50 | # PUBSPECS="./${MAIN_PUBSPEC}"
51 |
52 | # pushd packages
53 |
54 | # # We could use LAST_VERSION instead of allowing any previous version
55 |
56 | # # Update all references to package versions
57 | # perl -pi -e "s/^(\\W*package1:) \\^?[0-9.beta-]+/\$1 $VERSION/g" $PUBSPECS
58 | # perl -pi -e "s/^(\\W*package2:) \\^?[0-9.beta-]+/\$1 $VERSION/g" $PUBSPECS
59 | # perl -pi -e "s/^(\\W*package3:) \\^?[0-9.beta-]+/\$1 $VERSION/g" $PUBSPECS
60 | # perl -pi -e "s/^(\\W*package4:) \\^?[0-9.beta-]+/\$1 $VERSION/g" $PUBSPECS
61 | # perl -pi -e "s/^(\\W*package5:) \\^?[0-9.beta-]+/\$1 $VERSION/g" $PUBSPECS
62 |
63 | # popd
64 | # == END ====
--------------------------------------------------------------------------------
/org.purplegraphite.code/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion 29
30 | ndkVersion "21.0.6113669"
31 |
32 | sourceSets {
33 | main.java.srcDirs += 'src/main/kotlin'
34 | }
35 |
36 | lintOptions {
37 | disable 'InvalidPackage'
38 | checkReleaseBuilds false
39 | }
40 |
41 | defaultConfig {
42 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
43 | applicationId "org.purplegraphite.code"
44 | minSdkVersion 16
45 | targetSdkVersion 29
46 | versionCode flutterVersionCode.toInteger()
47 | versionName flutterVersionName
48 | }
49 |
50 | signingConfigs {
51 | release {
52 | storeFile file("../../release.jks")
53 | storePassword = "$System.env.STORE_PASSWORD"
54 | keyAlias = "$System.env.KEY_ALIAS"
55 | keyPassword = "$System.env.STORE_PASSWORD"
56 | }
57 | }
58 |
59 | buildTypes {
60 | release {
61 | // org.purplegraphite.code's own signing config for the release build.
62 | // Signing with the release keys, so `flutter run --release` works.
63 | signingConfig signingConfigs.release
64 | // Comment the above line and comment out line below when debugging without keys
65 | // signingConfig signingConfigs.debug
66 | }
67 | }
68 | }
69 |
70 | flutter {
71 | source '../..'
72 | }
73 |
74 | dependencies {
75 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
76 | }
77 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 | import 'dart:io';
3 |
4 | import 'package:code/src/utils/checksum.dart';
5 | import 'package:flutter_test/flutter_test.dart';
6 | import 'package:path/path.dart' as path;
7 |
8 | void prepareCollisionTestFiles(File file, String message) {
9 | if (!file.existsSync()) {
10 | file.createSync();
11 | }
12 | file.writeAsStringSync(
13 | message,
14 | mode: FileMode.writeOnly,
15 | encoding: utf8,
16 | );
17 | }
18 |
19 | void main() {
20 | group('File tests for collision', () {
21 | String currentDirectory;
22 | String test1Path, test2Path, test3Path;
23 | File test1, test2, test3;
24 | setUp(() {
25 | currentDirectory = Directory.current.path;
26 | test1Path = path.join(currentDirectory, 'test1.txt');
27 | test2Path = path.join(currentDirectory, 'test2.txt');
28 | test3Path = path.join(currentDirectory, 'test3.txt');
29 | test1 = File(test1Path);
30 | test2 = File(test2Path);
31 | test3 = File(test3Path);
32 | prepareCollisionTestFiles(test1, 'This is message 1');
33 | prepareCollisionTestFiles(test2, 'It\'s message 2');
34 | prepareCollisionTestFiles(test3, 'This is message 1');
35 | });
36 | test('by comparing a file\'s checksum with another file with same content',
37 | () async {
38 | final String reason =
39 | 'Checksums of same file (calculated twice) do not match!';
40 | final String digest1 = await Checksum.getDigest(test1);
41 | final String digest2 = await Checksum.getDigest(test3);
42 | expect(digest1, digest2, reason: reason);
43 | final String digest3 = Checksum.getDigestSync(test1);
44 | final String digest4 = Checksum.getDigestSync(test3);
45 | expect(digest3, digest4, reason: reason);
46 | });
47 | test('by comparing a file\'s checksum with other file\'s checksum',
48 | () async {
49 | final String reason = 'Checksums of both file matches!';
50 | final String digest1 = await Checksum.getDigest(test1);
51 | final String digest2 = await Checksum.getDigest(test2);
52 | expect(digest1 != digest2, true, reason: reason);
53 | expect(digest1 == digest2, false, reason: reason);
54 | final String digest3 = Checksum.getDigestSync(test1);
55 | final String digest4 = Checksum.getDigestSync(test2);
56 | expect(digest3 != digest4, true, reason: reason);
57 | expect(digest3 == digest4, false, reason: reason);
58 | });
59 | tearDown(() {
60 | test1.delete();
61 | test2.delete();
62 | test3.delete();
63 | });
64 | });
65 | }
66 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/models/view_model/browser_controller.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:code/src/models/plain_model/entity.dart';
4 | import 'package:code/src/utils/fileutils.dart';
5 | import 'package:flutter/widgets.dart';
6 | import 'package:path/path.dart' as path;
7 |
8 | class BrowserController extends ChangeNotifier {
9 | Directory _current;
10 |
11 | Directory get current => _current;
12 |
13 | List _currentEntities = [];
14 | List get currentEntities => _currentEntities;
15 | bool _stopLoading = false;
16 |
17 | bool get stopLoading => _stopLoading;
18 | List _recentlyCreatedFolder = [];
19 |
20 | // List of paths of newly created folder
21 | List get recentlyCreatedFolder => _recentlyCreatedFolder;
22 |
23 | /// Creates folder and updates listeners
24 | void createFolderAndAddToRecent(
25 | BuildContext context, String recentlyCreatedFolderBasename) async {
26 | var recentlyCreatedFolderPath =
27 | path.join(current.path, recentlyCreatedFolderBasename);
28 | await Directory.fromUri(
29 | Uri(
30 | path: recentlyCreatedFolderPath,
31 | ),
32 | ).create();
33 | _recentlyCreatedFolder.add(recentlyCreatedFolderPath);
34 | notifyListeners();
35 | await updateEntityList(context);
36 | }
37 |
38 | Future createFileAndAddToRecent(
39 | BuildContext context, String recentlyCreatedFolderBasename) async {
40 | var recentlyCreatedFolderPath =
41 | path.join(current.path, recentlyCreatedFolderBasename);
42 | final _file = await File.fromUri(
43 | Uri(
44 | path: recentlyCreatedFolderPath,
45 | ),
46 | ).create();
47 | _recentlyCreatedFolder.add(recentlyCreatedFolderPath);
48 | notifyListeners();
49 | await updateEntityList(context);
50 | return _file;
51 | }
52 |
53 | Future setCurrent(Directory dir) async {
54 | _current = dir;
55 | _currentEntities = await FileUtils.listEntities(_current);
56 | _stopLoading = true;
57 | notifyListeners();
58 | }
59 |
60 | Future updateEntityList(BuildContext context) async {
61 | if (!(await current.exists())) {
62 | Navigator.of(context).maybePop();
63 | return;
64 | }
65 | var _c = await FileUtils.listEntities(_current);
66 | if (_c.length == _currentEntities.length) {
67 | var same = true;
68 | for (var i = 0; i < _c.length; i++) {
69 | same = _c[i] == _currentEntities[i];
70 | if (!same) break;
71 | }
72 | if (same) return;
73 | }
74 |
75 | _currentEntities = _c;
76 | notifyListeners();
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/models/provider/history.dart:
--------------------------------------------------------------------------------
1 | import 'package:code/src/common/strings.dart';
2 | import 'package:code/src/models/hive/history.dart';
3 | import 'package:code/src/models/hive/repository.dart';
4 | import 'package:flutter/foundation.dart';
5 | import 'package:hive/hive.dart' show Box, Hive;
6 |
7 | class RecentHistoryProvider extends ChangeNotifier {
8 | RecentHistoryProvider() {
9 | _setup();
10 | }
11 |
12 | Repository _history;
13 |
14 | Box get box => _history.box;
15 |
16 | bool get hasHistory {
17 | return !(_history?.isRepositoryEmpty ?? true);
18 | }
19 |
20 | bool get isInitialized => _history != null;
21 |
22 | /// Sets up & Initializes preferences.
23 | Future _setup() async {
24 | Hive.registerAdapter(
25 | FileModificationHistoryAdapter());
26 | _history = await Repository.get(
27 | StorageBoxNames.HISTORY, HistoryAdapter());
28 |
29 | _history.listenStream((_) {
30 | notifyListeners();
31 | });
32 | notifyListeners();
33 | }
34 |
35 | History get(String path) {
36 | return _history.box.get(path);
37 | }
38 |
39 | List searchFor(String key) {
40 | final _histories = [];
41 | for (var item in _history.box.values) {
42 | final _itemID = item.workspacePath;
43 | if (key == _itemID) {
44 | _histories.insert(0, item);
45 | return _histories;
46 | } else if (_itemID.contains(key)) {
47 | _histories.add(item);
48 | }
49 | }
50 | return _histories;
51 | }
52 |
53 | List getHistories() {
54 | final _result = _history?.box?.values?.toList() ?? [];
55 | _result.sort();
56 | return _result;
57 | }
58 |
59 | void add(
60 | String path, [
61 | FileModificationHistory lastModifiedFileDetails,
62 | ]) async {
63 | final _projectHistory = History(workspacePath: path);
64 | _projectHistory.lastModifiedFileDetails = lastModifiedFileDetails;
65 | _projectHistory.updateLastModifiedDateTime();
66 | await _history.box.put(_projectHistory.workspacePath, _projectHistory);
67 | print(
68 | 'SAVED: ${_history.box.get(_projectHistory.workspacePath).workspacePath}');
69 | if (_projectHistory.isInBox) await _projectHistory.save();
70 | }
71 |
72 | Future remove(String path) async {
73 | if (box.containsKey(path)) {
74 | await box.delete(path);
75 | return true;
76 | } else {
77 | return false;
78 | }
79 | }
80 |
81 | Future update(History history) async {
82 | await box.put(history.workspacePath, history);
83 | await history.save();
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | # This is a basic workflow to help you get started with Actions
2 |
3 | name: Make release
4 |
5 | # Controls when the action will run. Triggers the workflow on push or pull request
6 | # events but only for the master branch
7 | on:
8 | push:
9 | tags:
10 | - "v*"
11 |
12 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel
13 | jobs:
14 | # This workflow contains a single job called "build"
15 | assembleRelease:
16 | # The type of runner that the job will run on
17 | runs-on: ubuntu-latest
18 |
19 | # Steps represent a sequence of tasks that will be executed as part of the job
20 | steps:
21 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
22 | - uses: actions/checkout@v2
23 | # Setup Java environment in order to build the Android app.
24 | - uses: actions/setup-java@v1
25 | with:
26 | java-version: "12.x"
27 | - uses: subosito/flutter-action@v1.5.3
28 | with:
29 | # The Flutter version to make available on the path
30 | flutter-version: 2.8.1 # optional
31 | # The Flutter build release channel
32 | channel: stable # optional, default is stable
33 |
34 | - name: Install NDK
35 | run: echo "y" | sudo /usr/local/lib/android/sdk/tools/bin/sdkmanager --install "ndk;21.0.6113669" --sdk_root=${ANDROID_SDK_ROOT}
36 |
37 | - name: Restore release key
38 | run: |
39 | cd $GITHUB_WORKSPACE/org.purplegraphite.code;
40 | echo "${{ secrets.KEYSTORE_JKS_ASC }}" > release.jks.asc;
41 | echo "Decrypting keystore";
42 | gpg -d --passphrase "${{ secrets.STORE_PASSWORD }}" --batch release.jks.asc > release.jks;
43 | rm release.jks.asc;
44 |
45 | - name: Build APKs
46 | env:
47 | KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
48 | STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
49 | # https://developer.android.com/studio/build/configure-apk-splits#configure-abi-split
50 | run: |
51 | cd $GITHUB_WORKSPACE/org.purplegraphite.code;
52 | flutter build apk --release --split-per-abi --split-debug-info=output/symbols;
53 |
54 | - name: Upload release
55 | uses: softprops/action-gh-release@v1
56 | with:
57 | prerelease: true
58 | body_path: RELEASE_NOTES.md
59 | name: "Pre-release"
60 | files: |
61 | org.purplegraphite.code/build/app/outputs/apk/release/app-arm64-v8a-release.apk
62 | org.purplegraphite.code/build/app/outputs/apk/release/app-armeabi-v7a-release.apk
63 | org.purplegraphite.code/build/app/outputs/apk/release/app-x86_64-release.apk
64 | env:
65 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
66 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/models/hive/history.g.dart:
--------------------------------------------------------------------------------
1 | // GENERATED CODE - DO NOT MODIFY BY HAND
2 |
3 | part of 'history.dart';
4 |
5 | // **************************************************************************
6 | // TypeAdapterGenerator
7 | // **************************************************************************
8 |
9 | class FileModificationHistoryAdapter
10 | extends TypeAdapter {
11 | @override
12 | final int typeId = 6;
13 |
14 | @override
15 | FileModificationHistory read(BinaryReader reader) {
16 | final numOfFields = reader.readByte();
17 | final fields = {
18 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
19 | };
20 | return FileModificationHistory(
21 | absolutePath: fields[1] as String,
22 | scrollOffset: fields[3] as double,
23 | cursorOffset: fields[4] as int,
24 | ).._lastModified = fields[2] as DateTime;
25 | }
26 |
27 | @override
28 | void write(BinaryWriter writer, FileModificationHistory obj) {
29 | writer
30 | ..writeByte(4)
31 | ..writeByte(1)
32 | ..write(obj.absolutePath)
33 | ..writeByte(2)
34 | ..write(obj._lastModified)
35 | ..writeByte(3)
36 | ..write(obj.scrollOffset)
37 | ..writeByte(4)
38 | ..write(obj.cursorOffset);
39 | }
40 |
41 | @override
42 | int get hashCode => typeId.hashCode;
43 |
44 | @override
45 | bool operator ==(Object other) =>
46 | identical(this, other) ||
47 | other is FileModificationHistoryAdapter &&
48 | runtimeType == other.runtimeType &&
49 | typeId == other.typeId;
50 | }
51 |
52 | class HistoryAdapter extends TypeAdapter {
53 | @override
54 | final int typeId = 5;
55 |
56 | @override
57 | History read(BinaryReader reader) {
58 | final numOfFields = reader.readByte();
59 | final fields = {
60 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
61 | };
62 | return History(
63 | workspacePath: fields[1] as String,
64 | )
65 | ..lastModifiedFileDetails = fields[2] as FileModificationHistory
66 | .._lastModified = fields[3] as DateTime;
67 | }
68 |
69 | @override
70 | void write(BinaryWriter writer, History obj) {
71 | writer
72 | ..writeByte(3)
73 | ..writeByte(1)
74 | ..write(obj.workspacePath)
75 | ..writeByte(2)
76 | ..write(obj.lastModifiedFileDetails)
77 | ..writeByte(3)
78 | ..write(obj._lastModified);
79 | }
80 |
81 | @override
82 | int get hashCode => typeId.hashCode;
83 |
84 | @override
85 | bool operator ==(Object other) =>
86 | identical(this, other) ||
87 | other is HistoryAdapter &&
88 | runtimeType == other.runtimeType &&
89 | typeId == other.typeId;
90 | }
91 |
--------------------------------------------------------------------------------
/.github/workflows/main_package.yml:
--------------------------------------------------------------------------------
1 | # This is a basic workflow to help you get started with Actions
2 |
3 | name: Flutter package analysis
4 |
5 | # Controls when the action will run. Triggers the workflow on push or pull request
6 | # events but only for the master branch
7 | on:
8 | push:
9 | branches: [ packages/* ]
10 | pull_request:
11 | branches: [ packages/*, master]
12 |
13 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel
14 | jobs:
15 | # This workflow contains a single job called "analysis"
16 | package_analysis:
17 | # The type of runner that the job will run on
18 | runs-on: ubuntu-latest
19 |
20 | # Steps represent a sequence of tasks that will be executed as part of the job
21 | steps:
22 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
23 | - uses: actions/checkout@v2
24 | # Setup Java environment in order to build the Android app.
25 | - uses: actions/setup-java@v1
26 | with:
27 | java-version: "12.x"
28 | - uses: subosito/flutter-action@v1.5.3
29 | with:
30 | # The Flutter version to make available on the path
31 | flutter-version: 2.8.1 # optional
32 | # The Flutter build release channel
33 | channel: stable # optional, default is stable
34 |
35 | - name: Flutter doctor
36 | run: flutter doctor -v;
37 |
38 | - name: Refreshing dependencies
39 | run: |
40 | cd $GITHUB_WORKSPACE/packages/creamy_field;
41 | flutter pub get;
42 |
43 | - name: Flutter code formatting check
44 | run: |
45 | cd $GITHUB_WORKSPACE/packages/creamy_field;
46 | flutter format --set-exit-if-changed .;
47 |
48 | - name: analyze issues
49 | run: |
50 | cd $GITHUB_WORKSPACE/packages/creamy_field;
51 | flutter analyze .;
52 |
53 | package_tests:
54 | # The type of runner that the job will run on
55 | runs-on: ubuntu-latest
56 |
57 | # Steps represent a sequence of tasks that will be executed as part of the job
58 | steps:
59 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
60 | - uses: actions/checkout@v2
61 | # Setup Java environment in order to build the Android app.
62 | - uses: actions/setup-java@v1
63 | with:
64 | java-version: "12.x"
65 | - uses: subosito/flutter-action@v1.5.3
66 | with:
67 | # The Flutter version to make available on the path
68 | flutter-version: 2.8.1 # optional
69 | # The Flutter build release channel
70 | channel: stable # optional, default is stable
71 | - name: run tests
72 | run: |
73 | cd $GITHUB_WORKSPACE/packages/creamy_field;
74 | flutter test;
75 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | # This is a basic workflow to help you get started with Actions
2 |
3 | name: Flutter analysis
4 |
5 | # Controls when the action will run. Triggers the workflow on push or pull request
6 | # events but only for the master branch
7 | on:
8 | push:
9 | branches: [master, dev/*]
10 | pull_request:
11 | branches: [master, dev/*]
12 |
13 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel
14 | jobs:
15 | # This workflow contains a single job called "analysis"
16 | analysis:
17 | # The type of runner that the job will run on
18 | runs-on: ubuntu-latest
19 |
20 | # Steps represent a sequence of tasks that will be executed as part of the job
21 | steps:
22 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
23 | - uses: actions/checkout@v2
24 | # Setup Java environment in order to build the Android app.
25 | - uses: actions/setup-java@v1
26 | with:
27 | java-version: "12.x"
28 | - uses: subosito/flutter-action@v1.5.3
29 | with:
30 | # The Flutter version to make available on the path
31 | flutter-version: 2.8.1 # optional
32 | # The Flutter build release channel
33 | channel: stable # optional, default is stable
34 |
35 | - name: Flutter doctor
36 | run: |
37 | cd $GITHUB_WORKSPACE/org.purplegraphite.code;
38 | flutter doctor -v;
39 |
40 | - name: Refreshing dependencies
41 | run: |
42 | cd $GITHUB_WORKSPACE/org.purplegraphite.code;
43 | flutter pub get;
44 |
45 | - name: Flutter code formatting check
46 | run: |
47 | cd $GITHUB_WORKSPACE/org.purplegraphite.code;
48 | flutter format --set-exit-if-changed .;
49 |
50 | - name: analyze issues
51 | run: |
52 | cd $GITHUB_WORKSPACE/org.purplegraphite.code;
53 | flutter analyze .;
54 |
55 | tests:
56 | # The type of runner that the job will run on
57 | runs-on: ubuntu-latest
58 |
59 | # Steps represent a sequence of tasks that will be executed as part of the job
60 | steps:
61 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
62 | - uses: actions/checkout@v2
63 | # Setup Java environment in order to build the Android app.
64 | - uses: actions/setup-java@v1
65 | with:
66 | java-version: "12.x"
67 | - uses: subosito/flutter-action@v1.5.3
68 | with:
69 | # The Flutter version to make available on the path
70 | flutter-version: 2.8.1 # optional
71 | # The Flutter build release channel
72 | channel: stable # optional, default is stable
73 | - name: run tests
74 | run: |
75 | cd $GITHUB_WORKSPACE/org.purplegraphite.code;
76 | flutter test;
77 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/ui/screens/history.dart:
--------------------------------------------------------------------------------
1 | import 'package:code/src/common/routing_const.dart';
2 | import 'package:code/src/models/provider/history.dart';
3 | import 'package:code/src/ui/screens/editor/controller.dart';
4 | import 'package:code/src/utils/theme.dart';
5 | import 'package:flutter/material.dart';
6 |
7 | import 'package:provider/provider.dart';
8 |
9 | class HistoryScreen extends StatelessWidget {
10 | @override
11 | Widget build(BuildContext context) {
12 | final _provider = Provider.of(context);
13 | final _histories = _provider.getHistories();
14 |
15 | final _isDarkMode = isDarkTheme(context);
16 | final _theme = Theme.of(context);
17 |
18 | final backgroundInDark = _isDarkMode ? Colors.black : Colors.white;
19 |
20 | final popupIconButtonColor = Color.lerp(_theme.accentColor,
21 | _isDarkMode ? Colors.white : Colors.black, _isDarkMode ? 0.10 : 0.25);
22 | final foregroundColorOnDarkBackground =
23 | _isDarkMode ? Colors.white.withOpacity(1) : Color(0xEE212121);
24 |
25 | final physics =
26 | BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics());
27 |
28 | return Scaffold(
29 | appBar: AppBar(
30 | title: Text('History'),
31 | ),
32 | body: ListView.builder(
33 | itemCount: _histories?.length == 0 ? 1 : _histories.length,
34 | physics: physics,
35 | itemBuilder: (context, index) {
36 | if (_histories?.isEmpty ?? true) {
37 | return Center(
38 | child: Padding(
39 | padding: const EdgeInsets.symmetric(vertical: 30.0),
40 | child: Text(
41 | 'You have no work history',
42 | style: TextStyle(
43 | color: foregroundColorOnDarkBackground,
44 | ),
45 | textAlign: TextAlign.center,
46 | ),
47 | ),
48 | );
49 | }
50 | final _history = _histories[index];
51 | return ListTile(
52 | title: Text(
53 | _history.workspacePath,
54 | style: TextStyle(
55 | color: foregroundColorOnDarkBackground,
56 | ),
57 | textAlign: TextAlign.center,
58 | ),
59 | onTap: () async {
60 | final settings =
61 | EditorSettings.fromDirectory(_history.workspacePath);
62 | await Provider.of(context, listen: false)
63 | .updateSettings(settings);
64 | Navigator.of(context).pushNamedAndRemoveUntil(
65 | EditorScreenRoute, (Route route) => false);
66 | },
67 | // Add a fuzzy timestamp
68 | // trailing: Text(_history.lastModified.),
69 | );
70 | },
71 | ),
72 | );
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
13 |
18 |
25 |
29 |
33 |
38 |
42 |
43 |
44 |
45 |
46 |
47 |
49 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/ui/screens/terminal.dart:
--------------------------------------------------------------------------------
1 | import 'package:code/src/models/provider/theme.dart';
2 | import 'package:code/src/models/view_model/terminal_controller.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter/widgets.dart';
5 | import 'package:provider/provider.dart';
6 |
7 | class TerminalScreen extends StatefulWidget {
8 | @override
9 | _TerminalScreenState createState() => _TerminalScreenState();
10 | }
11 |
12 | class _TerminalScreenState extends State {
13 | TerminalController controller;
14 | TextEditingController _commandController;
15 | @override
16 | void initState() {
17 | // TODO: implement initState
18 | super.initState();
19 | _commandController = TextEditingController();
20 | Provider.of(context, listen: false).start();
21 | }
22 |
23 | @override
24 | void didChangeDependencies() {
25 | super.didChangeDependencies();
26 | controller = Provider.of(context);
27 | }
28 |
29 | @override
30 | Widget build(BuildContext context) {
31 | TextStyle consoleText = ThemeProvider.monospaceTextStyle.merge(
32 | TextStyle(color: Colors.white),
33 | );
34 |
35 | return Scaffold(
36 | backgroundColor: Colors.grey[850],
37 | appBar: AppBar(
38 | title: Text('Terminal'),
39 | ),
40 | body: Column(
41 | children: [
42 | Expanded(
43 | // fit: FlexFit.loose,
44 | child: Padding(
45 | padding: const EdgeInsets.only(left: 4),
46 | // To make child scrollable
47 | child: SingleChildScrollView(
48 | scrollDirection: Axis.horizontal,
49 | physics: const ClampingScrollPhysics(),
50 | child: ConstrainedBox(
51 | constraints: const BoxConstraints.expand(width: 1500),
52 | child: ListView.builder(
53 | reverse: true,
54 | shrinkWrap: true,
55 | itemCount: controller.outputs.length,
56 | itemBuilder: (context, i) {
57 | var result =
58 | controller.outputs[controller.outputs.length - 1 - i];
59 | return Text(
60 | result.toString(),
61 | style: consoleText,
62 | );
63 | },
64 | ),
65 | ),
66 | ),
67 | ),
68 | ),
69 | TextField(
70 | controller: _commandController,
71 | keyboardType: TextInputType.text,
72 | textInputAction: TextInputAction.done,
73 | style: consoleText,
74 | decoration: InputDecoration(
75 | hintText: 'Enter commands here',
76 | hintStyle: consoleText.apply(color: Colors.white70)),
77 | onSubmitted: (_) async {
78 | await controller.execute(_);
79 | _commandController.clear();
80 | },
81 | ),
82 | ],
83 | ),
84 | );
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/models/view_model/terminal_controller.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 | import 'dart:io';
3 |
4 | import 'package:flutter/widgets.dart';
5 |
6 | class ConsoleOutput {
7 | final String command;
8 | List _outList = [];
9 |
10 | List get outputs => _outList;
11 |
12 | List _errList = [];
13 |
14 | List get errors => _errList;
15 |
16 | Map environment = {};
17 | ConsoleOutput(this.command);
18 | void addLog(Object object, {bool isError: false}) {
19 | if (isError) {
20 | _errList.add(object.toString());
21 | } else {
22 | _outList.add(object.toString());
23 | }
24 | }
25 |
26 | void updateEnv(Map env) {
27 | environment = env;
28 | }
29 |
30 | String toString() {
31 | return '${errors.join()}\n${outputs.join()}';
32 | }
33 | }
34 |
35 | class TerminalController extends ChangeNotifier {
36 | Map environment = {};
37 | // List _cache = [];
38 | List _outList = [];
39 |
40 | ConsoleOutput get lastConsoleLog {
41 | if (_outList.isEmpty) {
42 | return ConsoleOutput('');
43 | }
44 | return _outList.last;
45 | }
46 |
47 | List get outputs => _outList;
48 | final String prefix = '/data/data/org.basil.code';
49 | IOSink ios;
50 | Process _mainProcess;
51 | bool initialized = false;
52 | void ensureInitialized() {
53 | while (!initialized);
54 | }
55 |
56 | void start() async {
57 | var co = ConsoleOutput('sh -lva');
58 | _outList.add(co);
59 | _mainProcess = await Process.start(
60 | '/bin/sh',
61 | ['-ilva'],
62 | runInShell: true,
63 | environment: environment,
64 | ).then((result) {
65 | result.stdout.listen(comprehendStdout);
66 | result.stderr.listen(comprehendStderr);
67 | // result.stdin.addStream(result.stdout);
68 | return result;
69 | });
70 | ios = _mainProcess.stdin;
71 | initialized = true;
72 | co.updateEnv(environment);
73 | debug();
74 | notifyListeners();
75 | execute('cd $prefix');
76 | execute('clear');
77 | }
78 |
79 | void execute(String command) {
80 | if (command.contains('clear')) {
81 | // _cache = _outList;
82 | _outList = [];
83 | notifyListeners();
84 | return;
85 | }
86 | ensureInitialized();
87 | _outList.add(ConsoleOutput(command));
88 | ios.writeln(command);
89 | debug();
90 | }
91 |
92 | void debug() {
93 | print('Environment ${environment.keys.toList().join('\n')}');
94 | // print(_outList);
95 | }
96 |
97 | String decodeData(List codeUnits) {
98 | // final lines = utf8.decoder.convert(codeUnits);
99 | // .bind(File(path).openRead())
100 | // .transform(const LineSplitter());
101 | return utf8.decode(codeUnits);
102 | }
103 |
104 | void comprehendStdout(List data) {
105 | lastConsoleLog.addLog(decodeData(data));
106 | lastConsoleLog.updateEnv(environment);
107 | notifyListeners();
108 | }
109 |
110 | void comprehendStderr(List error) {
111 | lastConsoleLog.addLog(decodeData(error), isError: true);
112 | lastConsoleLog.updateEnv(environment);
113 | notifyListeners();
114 | }
115 |
116 | void clean() {
117 | ensureInitialized();
118 | ios.flush();
119 | ios.close();
120 | _mainProcess.kill();
121 | }
122 |
123 | @override
124 | void dispose() {
125 | super.dispose();
126 | clean();
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at smushaheed@outlook.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/ui/components/ask_dialog.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class _Consts {
4 | _Consts._();
5 |
6 | static const double padding = 16.0;
7 | static const double avatarRadius = 66.0;
8 | }
9 |
10 | Future showMessage(BuildContext context) {
11 | return showDialog(
12 | context: context,
13 | builder: (BuildContext context) => CustomDialog(
14 | title: "Success",
15 | description:
16 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
17 | buttonText: "Okay",
18 | ),
19 | );
20 | }
21 |
22 | class CustomDialog extends StatelessWidget {
23 | final String title, description, buttonText;
24 | final Image image;
25 |
26 | Widget dialogContent(BuildContext context) {
27 | return Stack(
28 | children: [
29 | //...bottom card part,
30 | Container(
31 | padding: EdgeInsets.only(
32 | top: _Consts.avatarRadius + _Consts.padding,
33 | bottom: _Consts.padding,
34 | left: _Consts.padding,
35 | right: _Consts.padding,
36 | ),
37 | margin: EdgeInsets.only(top: _Consts.avatarRadius),
38 | decoration: new BoxDecoration(
39 | color: Colors.white,
40 | shape: BoxShape.rectangle,
41 | borderRadius: BorderRadius.circular(_Consts.padding),
42 | boxShadow: [
43 | BoxShadow(
44 | color: Colors.black26,
45 | blurRadius: 10.0,
46 | offset: const Offset(0.0, 10.0),
47 | ),
48 | ],
49 | ),
50 | child: Column(
51 | mainAxisSize: MainAxisSize.min, // To make the card compact
52 | children: [
53 | Text(
54 | title,
55 | style: TextStyle(
56 | fontSize: 24.0,
57 | fontWeight: FontWeight.w700,
58 | ),
59 | ),
60 | SizedBox(height: 16.0),
61 | Text(
62 | description,
63 | textAlign: TextAlign.center,
64 | style: TextStyle(
65 | fontSize: 16.0,
66 | ),
67 | ),
68 | SizedBox(height: 24.0),
69 | Align(
70 | alignment: Alignment.bottomRight,
71 | child: FlatButton(
72 | onPressed: () {
73 | Navigator.of(context).pop(); // To close the dialog
74 | },
75 | child: Text(buttonText),
76 | ),
77 | ),
78 | ],
79 | ),
80 | ),
81 | //...top circlular image part,
82 | Positioned(
83 | left: _Consts.padding,
84 | right: _Consts.padding,
85 | child: CircleAvatar(
86 | backgroundColor: Colors.blueAccent,
87 | radius: _Consts.avatarRadius,
88 | ),
89 | ),
90 | ],
91 | );
92 | }
93 |
94 | CustomDialog({
95 | @required this.title,
96 | @required this.description,
97 | @required this.buttonText,
98 | this.image,
99 | });
100 |
101 | @override
102 | Widget build(BuildContext context) {
103 | return Dialog(
104 | shape: RoundedRectangleBorder(
105 | borderRadius: BorderRadius.circular(_Consts.padding),
106 | ),
107 | elevation: 0.0,
108 | backgroundColor: Colors.transparent,
109 | child: dialogContent(context),
110 | );
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/packages/creamy_field/lib/src/syntax_highlighter/language_type.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/foundation.dart';
2 |
3 | /// Language type which the syntax highlighting parser will use.
4 | enum LanguageType {
5 | /// This is actually 1C prgamming language. [LanguageType.language_1c] is
6 | /// just an alias of 1C language in this package __only__.
7 | language_1c,
8 | abnf,
9 | accesslog,
10 | actionscript,
11 | ada,
12 | all,
13 | angelscript,
14 | apache,
15 | applescript,
16 | arcade,
17 | arduino,
18 | armasm,
19 | asciidoc,
20 | aspectj,
21 | autohotkey,
22 | autoit,
23 | avrasm,
24 | awk,
25 | axapta,
26 | bash,
27 | basic,
28 | bnf,
29 | brainfuck,
30 | cal,
31 | capnproto,
32 | ceylon,
33 | clean,
34 | clojure,
35 | clojure_repl,
36 | cmake,
37 | coffeescript,
38 | coq,
39 | cos,
40 | cpp,
41 | crmsh,
42 | crystal,
43 | cs,
44 | csp,
45 | css,
46 | dart,
47 | d,
48 | delphi,
49 | diff,
50 | django,
51 | dns,
52 | dockerfile,
53 | dos,
54 | dsconfig,
55 | dts,
56 | dust,
57 | ebnf,
58 | elixir,
59 | elm,
60 | erb,
61 | erlang,
62 | erlang_repl,
63 | excel,
64 | fix,
65 | flix,
66 | fortran,
67 | fsharp,
68 | gams,
69 | gauss,
70 | gcode,
71 | gherkin,
72 | glsl,
73 | gml,
74 | gn,
75 | go,
76 | golo,
77 | gradle,
78 | graphql,
79 | groovy,
80 | haml,
81 | handlebars,
82 | haskell,
83 | haxe,
84 | hsp,
85 | htmlbars,
86 | http,
87 | hy,
88 | inform7,
89 | ini,
90 | irpf90,
91 | isbl,
92 | java,
93 | javascript,
94 | jboss_cli,
95 | json,
96 | julia,
97 | julia_repl,
98 | kotlin,
99 | lasso,
100 | ldif,
101 | leaf,
102 | less,
103 | lisp,
104 | livecodeserver,
105 | livescript,
106 | llvm,
107 | lsl,
108 | lua,
109 | makefile,
110 | markdown,
111 | mathematica,
112 | matlab,
113 | maxima,
114 | mel,
115 | mercury,
116 | mipsasm,
117 | mizar,
118 | mojolicious,
119 | monkey,
120 | moonscript,
121 | n1ql,
122 | nginx,
123 | nimrod,
124 | nix,
125 | nsis,
126 | objectivec,
127 | ocaml,
128 | openscad,
129 | oxygene,
130 | parser3,
131 | perl,
132 | pf,
133 | pgsql,
134 | php,
135 | plaintext,
136 | pony,
137 | powershell,
138 | processing,
139 | profile,
140 | prolog,
141 | properties,
142 | protobuf,
143 | puppet,
144 | purebasic,
145 | python,
146 | q,
147 | qml,
148 | r,
149 | reasonml,
150 | rib,
151 | roboconf,
152 | routeros,
153 | rsl,
154 | ruby,
155 | ruleslanguage,
156 | rust,
157 | sas,
158 | scala,
159 | scheme,
160 | scilab,
161 | scss,
162 | shell,
163 | smali,
164 | smalltalk,
165 | sml,
166 | solidity,
167 | sqf,
168 | sql,
169 | stan,
170 | stata,
171 | step21,
172 | stylus,
173 | subunit,
174 | swift,
175 | taggerscript,
176 | tap,
177 | tcl,
178 | tex,
179 | thrift,
180 | tp,
181 | twig,
182 | typescript,
183 | vala,
184 | vbnet,
185 | vbscript,
186 | vbscript_html,
187 | verilog,
188 | vhdl,
189 | vim,
190 | vue,
191 | x86asm,
192 | xl,
193 | xml,
194 | xquery,
195 | yaml,
196 | zephir,
197 | }
198 |
199 | /// Describes the language name.
200 | ///
201 | /// Strips off the enum class name from the `LanguageType.toString()` and
202 | /// returns a proper name for the syntax highlighter's parser.
203 | String toLanguageName(LanguageType enumEntry) {
204 | final String language = describeEnum(enumEntry);
205 |
206 | // handle exceptionals
207 | if (language.isEmpty) return 'all';
208 |
209 | switch (language) {
210 | case "language_1c":
211 | return "1c";
212 | default:
213 | return language.replaceAll('_', '-');
214 | }
215 | }
216 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/common/routes.dart:
--------------------------------------------------------------------------------
1 | import 'package:code/src/ui/components/start_tips/tips.dart';
2 | import 'package:code/src/ui/screens/editor/editor.dart';
3 | import 'package:code/src/ui/screens/history.dart';
4 | import 'package:code/src/ui/screens/start/controller.dart';
5 | import 'package:code/src/ui/screens/start/start.dart';
6 | import 'package:code/src/ui/screens/workspace_explorer.dart';
7 | import 'package:flutter/cupertino.dart';
8 | import 'package:flutter/material.dart';
9 | import 'package:provider/provider.dart';
10 |
11 | // Constant String screen's path address
12 | import '../common/routing_const.dart';
13 |
14 | // Screens
15 | import '../../main.dart';
16 | import '../ui/screens/browser.dart';
17 | import '../ui/screens/settings.dart';
18 | import '../ui/screens/terminal.dart';
19 |
20 | // Models
21 | import '../models/view_model/browser_controller.dart';
22 | import '../models/view_model/terminal_controller.dart';
23 |
24 | /// Wraps [screen] with a [PageRoute]
25 | PageRoute wrapPageRoute(Widget screen,
26 | [bool useCupertinoPageRoute = true]) {
27 | if (useCupertinoPageRoute) {
28 | return CupertinoPageRoute(builder: (context) => screen);
29 | }
30 | return MaterialPageRoute(
31 | builder: (context) => screen,
32 | );
33 | }
34 |
35 | /// Generates Routes which will be used in the application
36 | Route generateRoute(RouteSettings settings) {
37 | switch (settings.name) {
38 | case StartScreenRoute:
39 | return wrapPageRoute(
40 | MultiProvider(
41 | providers: [
42 | ChangeNotifierProvider(
43 | create: (context) => StartTipsController(),
44 | ),
45 | ChangeNotifierProvider(
46 | create: (context) => StartScreenController(),
47 | ),
48 | ],
49 | child: StartScreen(),
50 | ),
51 | );
52 | case EditorScreenRoute:
53 | return wrapPageRoute(EditorScreen());
54 | case BrowserScreenRoute:
55 | return wrapPageRoute(
56 | ChangeNotifierProvider(
57 | create: (context) => BrowserController(),
58 | child: BrowserScreen(
59 | dir: settings.arguments,
60 | ),
61 | ),
62 | );
63 | case SettingsScreenRoute:
64 | return wrapPageRoute(SettingsScreen());
65 | case WorkspaceExplorerScreenRoute:
66 | return wrapPageRoute(
67 | ChangeNotifierProvider(
68 | create: (context) => BrowserController(),
69 | child: WorkspaceExplorerScreen(dir: settings.arguments),
70 | ),
71 | );
72 | case TerminalScreenRoute:
73 | return wrapPageRoute(
74 | ChangeNotifierProvider(
75 | create: (context) => TerminalController(),
76 | child: TerminalScreen(),
77 | ),
78 | );
79 | case HistoryScreenRoute:
80 | return wrapPageRoute(HistoryScreen());
81 | case RootRoute:
82 | default:
83 | // TODO(mushaheedx): Reroute from Root to Start instead of replacing a widget in Root with Start
84 | return wrapPageRoute(
85 | MultiProvider(
86 | providers: [
87 | ChangeNotifierProvider(
88 | create: (context) => StartTipsController(),
89 | ),
90 | ChangeNotifierProvider(
91 | create: (context) => StartScreenController(),
92 | ),
93 | ],
94 | child: Root(
95 | key: RootRouteKey,
96 | ),
97 | ),
98 | );
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | xmlns:android
14 |
15 | ^$
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | xmlns:.*
25 |
26 | ^$
27 |
28 |
29 | BY_NAME
30 |
31 |
32 |
33 |
34 |
35 |
36 | .*:id
37 |
38 | http://schemas.android.com/apk/res/android
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | .*:name
48 |
49 | http://schemas.android.com/apk/res/android
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | name
59 |
60 | ^$
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | style
70 |
71 | ^$
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | .*
81 |
82 | ^$
83 |
84 |
85 | BY_NAME
86 |
87 |
88 |
89 |
90 |
91 |
92 | .*
93 |
94 | http://schemas.android.com/apk/res/android
95 |
96 |
97 | ANDROID_ATTRIBUTE_ORDER
98 |
99 |
100 |
101 |
102 |
103 |
104 | .*
105 |
106 | .*
107 |
108 |
109 | BY_NAME
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Snake Code (org.purplegraphite.code)
2 |
3 | _Effortlessly programming_ on mobile
4 |
5 | [](https://github.com/predatorx7/snake_code/actions?query=workflow%3A%22Flutter+analysis%22)
6 |
7 | Snake Code is just a simple source code editor. This project's main target platform is Android.
8 |
9 | The application is written in dart (uses Flutter UI Toolkit).
10 |
11 | ## Objective
12 |
13 | - An easy to use source code editor
14 | - Open directories/files as projects
15 | - Syntax highlighting
16 | - Basic editing tools for ex. Find & replace
17 |
18 | ---
19 |
20 | ## Structure
21 |
22 | ```tree
23 | ├── org.purplegraphite.code
24 | └── packages
25 | └── creamy_field
26 | ```
27 |
28 | ### org.purplegraphite.code
29 |
30 | The main flutter project code resides here.
31 |
32 | ### _packages_
33 |
34 | All the internal and general purpose packages used in org.purplegraphite.code is here.
35 |
36 | - creamy_field
37 |
38 | The package provides components & widgets with rich text, custom selection toolbar & syntax highlight support.
39 |
40 | ---
41 |
42 | ## A brief information about the project
43 |
44 | ### Problem this tries to solves
45 |
46 | This project aims to help make programming on mobile devices easier.
47 |
48 | It will focus on being flexible in functionality by providing various in-app settings for users and allowing support for extensions in future.
49 | Extensions built by us or others can improve suggestions, performance, theming, etc.
50 |
51 | It helps in modifying a source code of a project simple by allowing to edit multiple files at once in tabs and switching between them quicker in a nice interface.
52 |
53 | It'll include a file directory explorer for faster exploration of files. Simple to use yet powerful search, replace, and other tools to aid in writing.
54 |
55 | ### Some functionalities
56 |
57 | 1. A project explorer to help browsing files a user needs faster and less messy. Existing similar applications show all files in a project as an expandable tile which makes the list too long & wide making it difficult to distinguish between files of one folder with another folder.
58 | 1. A search tool to globally search for files with names, or files with a text equal or similar to the searched query.
59 | 1. A tab switcher to change between current tabs on screen quickly similar to a web browser.
60 | 1. Flexibility in functionality with a lot of options in settings and support for extensions to change themes, syntax highlighting, way of debugging. The app will also be flexible in importing a project and exporting/sharing it in a portable but fast format.
61 |
62 | ### Users
63 |
64 | The type of users which may use this app.
65 |
66 | 1. Users who don't have money to buy a computer for an indefinite amount of time but have a mobile device available.
67 | 1. Programmers who need to edit a project on a mobile device because they are commuting in a transport or due to temporary unavailability of a computer.
68 | 1. Users who just want to quickly run and share a small program with each other in groups.
69 | 1. Users who don’t want to waste money on buying a computer if the app is able to run projects on mobile.
70 |
71 | Users (likely students) from an economically weak background wouldn’t be able to afford a full fledged computer but could have access to a mobile. These users can use this app and try to achieve relevant skills by learning and developing programs. My app will try to not let an unavailability of a computer for an indefinite amount of time be an obstacle for these users.
72 |
73 | ### Developer goals for this app's design
74 |
75 | 1. Build community for extension development for making this app more functionally flexible and vast.
76 | 1. Keep this app as an economically cheap alternative for program development for users.
77 | 1. A medium to quickly run code and share it with other users.
78 | 1. Fast in its functions to improve user’s productivity.
79 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/ui/screens/editor_tab.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:code/src/models/plain_model/entity.dart';
4 | import 'package:code/src/models/provider/theme.dart';
5 | import 'package:creamy_field/creamy_field.dart';
6 | import 'package:flutter/material.dart';
7 |
8 | class EditorTabController with ChangeNotifier {
9 | String get directoryPath => file.path;
10 |
11 | final Entity entity;
12 | File get file => entity.entity as File;
13 |
14 | ScrollController scrollController;
15 | FocusNode focusNode;
16 | CreamyEditingController textController;
17 |
18 | bool _initialized = false;
19 | bool get isInitialized => _initialized;
20 |
21 | void initState() {
22 | scrollController = ScrollController();
23 | textController = CreamyEditingController();
24 | focusNode = FocusNode();
25 | _initialized = true;
26 | }
27 |
28 | @override
29 | void dispose() {
30 | scrollController.dispose();
31 | focusNode.dispose();
32 | textController.dispose();
33 | super.dispose();
34 | _initialized = false;
35 | }
36 |
37 | EditorTabController(this.entity);
38 | }
39 |
40 | class EditorTab extends StatefulWidget with Comparable {
41 | final EditorTabController controller;
42 |
43 | const EditorTab._({
44 | @required this.controller,
45 | Key key,
46 | }) : super(key: key);
47 |
48 | factory EditorTab.fromFile(File file) {
49 | return EditorTab._(
50 | controller: EditorTabController(Entity(file)),
51 | );
52 | }
53 |
54 | @override
55 | int compareTo(EditorTab other) {
56 | return this.controller.entity.compareTo(other.controller.entity);
57 | }
58 |
59 | @override
60 | _EditorTabState createState() =>
61 | _EditorTabState(ValueKey(controller.directoryPath));
62 | }
63 |
64 | class _EditorTabState extends State {
65 | final ValueKey key;
66 |
67 | _EditorTabState(this.key);
68 |
69 | @override
70 | void initState() {
71 | widget.controller.initState();
72 | super.initState();
73 | }
74 |
75 | @override
76 | void dispose() {
77 | widget.controller.dispose();
78 | super.dispose();
79 | }
80 |
81 | @override
82 | Widget build(BuildContext context) {
83 | final foregroundInDark = (Theme.of(context).brightness == Brightness.dark)
84 | ? Colors.white
85 | : Colors.black;
86 | return LineCountIndicator(
87 | textControllerOfTextField: widget.controller.textController,
88 | scrollControllerOfTextField: widget.controller.scrollController,
89 | decoration: LineCountIndicatorDecoration(
90 | textStyle: ThemeProvider.monospaceTextStyle,
91 | ),
92 | child: HorizontalScrollable(
93 | child: TextField(
94 | key: key,
95 | controller: widget.controller.textController,
96 | keyboardType: TextInputType.multiline,
97 | textInputAction: TextInputAction.newline,
98 | textCapitalization: TextCapitalization.none,
99 | textAlign: TextAlign.left,
100 | textDirection: TextDirection.ltr,
101 | obscureText: false,
102 | focusNode: widget.controller.focusNode,
103 | style: ThemeProvider.monospaceTextStyle
104 | .copyWith(color: foregroundInDark),
105 | autocorrect: true,
106 | enableSuggestions: true,
107 | maxLines: null,
108 | scrollPadding: const EdgeInsets.all(20.0),
109 | smartDashesType: SmartDashesType.enabled,
110 | smartQuotesType: SmartQuotesType.enabled,
111 | textAlignVertical: TextAlignVertical.top,
112 | scrollController: widget.controller.scrollController,
113 | decoration: InputDecoration.collapsed(
114 | hintText: 'Start writing..',
115 | hintStyle: ThemeProvider.monospaceTextStyle.copyWith(
116 | color: foregroundInDark.withOpacity(0.5),
117 | ),
118 | ),
119 | ),
120 | ),
121 | );
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/ui/components/newfolder_dialog.dart:
--------------------------------------------------------------------------------
1 | import 'package:fluentui_system_icons/fluentui_system_icons.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | class _Consts {
5 | _Consts._();
6 |
7 | static const double padding = 16.0;
8 | static const double avatarRadius = 40.0;
9 | }
10 |
11 | Future newFolderDialog(
12 | BuildContext context,
13 | void Function(TextEditingController controller) onButtonPress, [
14 | bool file = false,
15 | ]) {
16 | return showDialog(
17 | context: context,
18 | builder: (BuildContext context) => CustomDialog(
19 | title: "Enter ${file ? 'file' : 'folder'} name",
20 | buttonText: "Create",
21 | onPressed: onButtonPress,
22 | ),
23 | );
24 | }
25 |
26 | class CustomDialog extends StatelessWidget {
27 | final String title, buttonText;
28 | final Image image;
29 | final void Function(TextEditingController controller) onPressed;
30 | final TextEditingController controller = TextEditingController();
31 | Widget dialogContent(BuildContext context) {
32 | return Stack(
33 | children: [
34 | //...bottom card part,
35 | Container(
36 | padding: EdgeInsets.only(
37 | top: _Consts.avatarRadius + _Consts.padding,
38 | bottom: _Consts.padding,
39 | left: _Consts.padding,
40 | right: _Consts.padding,
41 | ),
42 | margin: EdgeInsets.only(top: _Consts.avatarRadius),
43 | decoration: new BoxDecoration(
44 | color: Colors.white,
45 | shape: BoxShape.rectangle,
46 | borderRadius: BorderRadius.circular(_Consts.padding),
47 | boxShadow: [
48 | BoxShadow(
49 | color: Colors.black26,
50 | blurRadius: 10.0,
51 | offset: const Offset(0.0, 10.0),
52 | ),
53 | ],
54 | ),
55 | child: Column(
56 | mainAxisSize: MainAxisSize.min, // To make the card compact
57 | children: [
58 | Text(
59 | title,
60 | style: TextStyle(
61 | fontSize: 18.0,
62 | fontWeight: FontWeight.w700,
63 | ),
64 | ),
65 | SizedBox(height: 16.0),
66 | TextField(
67 | controller: controller,
68 | textAlign: TextAlign.center,
69 | style: TextStyle(
70 | fontSize: 16.0,
71 | ),
72 | decoration: InputDecoration(),
73 | ),
74 | SizedBox(height: 24.0),
75 | Align(
76 | alignment: Alignment.bottomRight,
77 | child: FlatButton(
78 | onPressed: () {
79 | onPressed(controller);
80 | Navigator.of(context).pop(); // To close the dialog
81 | },
82 | child: Text(buttonText),
83 | ),
84 | ),
85 | ],
86 | ),
87 | ),
88 | //...top circlular image part,
89 | Positioned(
90 | left: _Consts.padding,
91 | right: _Consts.padding,
92 | child: CircleAvatar(
93 | backgroundColor: Theme.of(context).accentColor,
94 | radius: _Consts.avatarRadius,
95 | child: Icon(
96 | FluentIcons.folder_add_28_regular,
97 | size: 28,
98 | ),
99 | ),
100 | ),
101 | ],
102 | );
103 | }
104 |
105 | CustomDialog({
106 | @required this.title,
107 | @required this.onPressed,
108 | @required this.buttonText,
109 | this.image,
110 | });
111 |
112 | @override
113 | Widget build(BuildContext context) {
114 | return Dialog(
115 | shape: RoundedRectangleBorder(
116 | borderRadius: BorderRadius.circular(_Consts.padding),
117 | ),
118 | elevation: 0.0,
119 | backgroundColor: Colors.transparent,
120 | child: dialogContent(context),
121 | );
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:code/src/models/provider/history.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:provider/provider.dart';
4 |
5 | import 'src/common/routes.dart';
6 | import 'src/models/provider/theme.dart';
7 | import 'src/ui/screens/editor/controller.dart';
8 | import 'src/ui/screens/start/start.dart';
9 | import 'src/utils/permissions.dart';
10 |
11 | void main() async {
12 | runApp(App());
13 | }
14 |
15 | /// Starting point for our app
16 | class App extends StatelessWidget {
17 | // Usually, without keys widgets unmounts and then mounts again and rebuilds.
18 | // This behaviour may creates performance issues.
19 | // Thus, using keys below may cause widgets to update instead of remounting.
20 | // Using keys isn't neccessary as Flutter is fast but we don't want to remount
21 | // heavy widgets hence the usage
22 | final _appKey = GlobalKey();
23 | final GlobalKey _navigatorKey = GlobalKey();
24 |
25 | final _recents = RecentHistoryProvider();
26 |
27 | @override
28 | Widget build(BuildContext context) {
29 | final _themeProvider = ThemeProvider(
30 | navigatorKey: _navigatorKey,
31 | );
32 |
33 | // The [MultiProvider] builds providers which instances of object throughout
34 | // the widget tree with a search complexity of O(1)
35 | return MultiProvider(
36 | providers: [
37 | // Provides instance of class initiated at the create parameter
38 | ChangeNotifierProvider.value(
39 | // Provides theme to the descendant widgets.
40 | // Use Provider.of(context) to get it's instance.
41 | value: _themeProvider,
42 | ),
43 | ChangeNotifierProvider(
44 | create: (_) => EditorController(_recents, _themeProvider),
45 | ),
46 | ChangeNotifierProvider(
47 | create: (_) => _recents,
48 | )
49 | ],
50 | child: Consumer(
51 | builder: (context, th, child) {
52 | return MaterialApp(
53 | key: _appKey,
54 |
55 | /// Will be used to catch intents, and to hanle Routes
56 | /// without context
57 | navigatorKey: _navigatorKey,
58 | title: 'Snake editor',
59 | theme: th.currentLightTheme,
60 | darkTheme: th.currentDarkTheme,
61 | themeMode: th.themeMode,
62 | // Don't need to explicitly specify home as [generateRoute] does it.
63 | onGenerateRoute: generateRoute,
64 | );
65 | },
66 | ),
67 | );
68 | }
69 | }
70 |
71 | /// The main root widget route which will ask permissions and show notifications
72 | /// on load
73 | class Root extends StatefulWidget {
74 | /// This root will show notifications and check storage access permissions.
75 | /// If the permissions have been aapproved, bg widget will replace itself
76 | /// with [StartScreen]
77 | const Root({Key key}) : super(key: key);
78 | @override
79 | _RootState createState() => _RootState();
80 | }
81 |
82 | class _RootState extends State {
83 | final ValueKey _mainKey = const ValueKey('mainScreen');
84 | bool perms = false;
85 |
86 | /// Ask permissions & then change [perms] to true
87 | Future _requestPermissions() async {
88 | // Show dialog here before requesting permissions.
89 | await Perms.askOnce();
90 | setState(() {
91 | perms = true;
92 | });
93 | }
94 |
95 | void initState() {
96 | super.initState();
97 | WidgetsBinding.instance.addPostFrameCallback((_) => _requestPermissions());
98 | }
99 |
100 | @override
101 | Widget build(BuildContext context) {
102 | Widget start = StartScreen(
103 | key: _mainKey,
104 | );
105 |
106 | // TODO(predatorx7): Show a dialog to describe why you need permissions.
107 | return Visibility(
108 | visible: perms,
109 | child: start,
110 | // Shows this widget until [perms] is true
111 | replacement: Scaffold(
112 | body: Center(
113 | child: CircularProgressIndicator(),
114 | ),
115 | ),
116 | );
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/packages/creamy_field/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:creamy_field/creamy_field.dart'; // imported the package
3 |
4 | void main() {
5 | final ThemeData _theme = ThemeData(primarySwatch: Colors.blue);
6 | runApp(MaterialApp(
7 | home: MyEditorApp(),
8 | // Change theme mode to try syntax highlighting colors in dark mode & light mode
9 | themeMode: ThemeMode.light,
10 | theme: ThemeData(primarySwatch: Colors.blue),
11 | darkTheme: _theme.copyWith(brightness: Brightness.dark),
12 | ));
13 | }
14 |
15 | class MyEditorApp extends StatefulWidget {
16 | @override
17 | _MyEditorAppState createState() => _MyEditorAppState();
18 | }
19 |
20 | class _MyEditorAppState extends State {
21 | // Declared a regular syntax controller.
22 | late CreamyEditingController controller;
23 | late ScrollController scrollController;
24 |
25 | @override
26 | void initState() {
27 | super.initState();
28 |
29 | // The below example shows [CreamyEditingController], a text editing controller with RichText highlighting support
30 | controller = CreamyEditingController(
31 | // This is the CreamySyntaxHighlighter which will be used by the controller
32 | // to generate list of RichText for syntax highlighting
33 | syntaxHighlighter: CreamySyntaxHighlighter(
34 | language: LanguageType.dart,
35 | theme: HighlightedThemeType.defaultTheme,
36 | ),
37 | // The number of spaces which will replace `\t`.
38 | // Setting this to 1 does nothing & setting this to value less than 1
39 | // throws assertion error.
40 | tabSize: 4,
41 | );
42 | scrollController = ScrollController();
43 | }
44 |
45 | @override
46 | void dispose() {
47 | controller.dispose();
48 | scrollController.dispose();
49 | super.dispose();
50 | }
51 |
52 | @override
53 | Widget build(BuildContext context) {
54 | bool _isDark = Theme.of(context).brightness == Brightness.dark;
55 | return new Scaffold(
56 | backgroundColor: _isDark ? Colors.black : Colors.white,
57 | appBar: new AppBar(
58 | title: new Text("Rich Code Editor"),
59 | actions: [
60 | TextButton(
61 | child: Text('Add tab'),
62 | onPressed: () {
63 | // Adds a tab at the selection's base base-offet
64 | controller.addTab();
65 | },
66 | )
67 | ],
68 | ),
69 | // Shows line indicator column adjacent to this widget
70 | body: LineCountIndicator(
71 | textControllerOfTextField: controller,
72 | scrollControllerOfTextField: scrollController,
73 | decoration: LineCountIndicatorDecoration(
74 | backgroundColor: Colors.grey,
75 | ),
76 | // Allow this Text field to be horizontally scrollable
77 | child: HorizontalScrollable(
78 | // Additional options for text selection widget
79 | child: TextField(
80 | autofocus: true,
81 | // Our controller should be up casted as CreamyEditingController
82 | // Note: Declare controller as CreamyEditingController if this fails.
83 | controller: controller,
84 | scrollController: scrollController,
85 | textCapitalization: TextCapitalization.none,
86 | decoration: InputDecoration.collapsed(hintText: 'Start writing'),
87 | maxLines: null,
88 | selectionControls: CreamyTextSelectionControlsProvider(
89 | type: TextSelectionControlsType.material,
90 | actionsBuilder: (_, __, ___) {
91 | return [
92 | CreamyTextSelectionToolbarAction(
93 | label: 'Button1',
94 | onPressed: () {
95 | print('Button2');
96 | },
97 | ),
98 | CreamyTextSelectionToolbarAction(
99 | label: 'Button2',
100 | onPressed: () {
101 | print('Button2');
102 | },
103 | ),
104 | ];
105 | },
106 | ),
107 | ),
108 | ),
109 | ),
110 | );
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/packages/creamy_field/README.md:
--------------------------------------------------------------------------------
1 | # creamy_field
2 |
3 | [](https://pub.dev/packages/creamy_field)
4 | [](https://github.com/predatorx7/snake_code/issues)
5 | [](https://github.com/predatorx7/snake_code/actions?query=workflow%3A%22Flutter+package+analysis%22)
6 |
7 | Components & widgets with rich text, custom selection toolbar & syntax highlight support. Useful for Rich text editors.
8 |
9 | ## Getting Started
10 |
11 | ### The main components of this package
12 |
13 | 1. [CreamyTextField (REMOVED)](#CreamyTextField)
14 | 1. [CreamyEditingController](#CreamyEditingController)
15 | 1. [Syntax Highlighter](#CreamySyntaxHighlighter)
16 | 1. [LineCountIndicator](#LineCountIndicator)
17 | 1. [HorizontalScrollable](#HorizontalScrollable)
18 | 1. [CreamyTextSelectionControlsProvider](#CreamyTextSelectionControlsProvider)
19 |
20 | The creamy_field package uses some code from flutter to keep API similar/compatible with other components in flutter.
21 |
22 | #### CreamyTextField
23 |
24 | This widget has been removed.
25 |
26 | The CreamyTextField was a text widget similar to Flutter's `TextField` widget with additional features. However, with update to flutter v2.x.x, we don't need this anymore. Components from this package can be used with flutter library to provide additional functionality while editing text.
27 |
28 | #### CreamySyntaxHighlighter
29 |
30 | You can use a limited support for syntax highlighting of many programming languages & themes using CreamySyntaxHighlighter.
31 |
32 | Since, the text field itself is independent of the syntax highlighting rules, you will only need to implement the syntax highlighter implementation separately for your custom syntax and provide this to the controller.
33 |
34 | #### CreamyEditingController
35 |
36 | The CreamyEditingController is responsible for changing tab sizes, applyng syntax highlighting to text and providing other useful information like line count.
37 |
38 | You can use CreamyEditingController as TextEditingController in regular TextFields/TextFormFields of flutter. Syntax highlighting will work on them too.
39 |
40 | Some text features described above are provided by an extension on TextEditingController. Check [CreamyTextFieldExtensions].
41 |
42 | #### LineCountIndicator
43 |
44 | A horizontal widget with lists of indexes to represent adjacent TextField's line number.
45 |
46 | A [LineCountIndicatorDecoration] decoration can be applied to this widget.
47 |
48 | Make sure that the decoration uses the same font-family & font-size from the TextField.
49 |
50 | #### HorizontalScrollable
51 |
52 | Makes a child widget horizontally scrollable. Developed with intent of wrapping a TextField to make it horizontally scrollable.
53 |
54 | In the future, the controller in this widget which is provided to a child TextField will be used to determine horizontal scroll extent.
55 |
56 | #### CreamyTextSelectionControlsProvider
57 |
58 | The class can be used to add additional toolbar actions to a selection toolbar in a text field.
59 |
60 | This package provides several text selection controls (only 1 in v0.4.0) via CreamyTextSelectionControls. You can create your own selection controls which supports additional toolbar actions by mixing with [CreamyTextSelectionControls] and providing that in [CreamyTextSelectionControlsProvider.custom].
61 |
62 | ### Note
63 |
64 | - Versions **before** v0.4.0 is not compatible with flutter v2.x.x due to a lot of breaking changes introduced in Text editing APIs. We'll try to keep the API stable in v0.4.0 and above.
65 |
66 | - Use [creamy_field v0.3.3](https://pub.dev/packages/creamy_field/versions/0.3.3) if you're using flutter sdk `>=1.22.0 <2.0.0`
67 |
68 | - Use [creamy_field v0.3.2](https://pub.dev/packages/creamy_field/versions/0.3.2) if you're using flutter sdk `>1.20.0 <1.22.0`
69 |
70 | - Use [creamy_field v0.3.1](https://pub.dev/packages/creamy_field/versions/0.3.1) if you're using flutter sdk `<=1.20.0`
71 |
72 |
73 |
74 | Check [screenshots folder](https://github.com/predatorx7/snake_code/tree/master/packages/creamy_field/screenshots) for some sample UI screenshots.
75 |
76 | Feel free to add features, [issues](https://github.com/predatorx7/snake_code/issues) & [pull request](https://github.com/predatorx7/snake_code/pulls)
77 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/ui/components/drawer/editor_drawer.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:code/src/common/routing_const.dart';
4 | import 'package:fluentui_system_icons/fluentui_system_icons.dart';
5 | import 'package:flutter/material.dart';
6 |
7 | class ColoredListTile extends StatelessWidget {
8 | final bool isDark;
9 | final MaterialColor color;
10 | final void Function() onPressed;
11 | final Widget title;
12 | final Widget leading;
13 |
14 | const ColoredListTile(
15 | this.isDark, this.color, this.onPressed, this.title, this.leading,
16 | {Key key})
17 | : super(key: key);
18 |
19 | Widget build(BuildContext context) {
20 | return Container(
21 | color: isDark ? color[700] : color[500],
22 | child: OutlineButton(
23 | padding: EdgeInsets.zero,
24 | borderSide: BorderSide.none,
25 | splashColor: color[600],
26 | onPressed: onPressed,
27 | child: ListTile(
28 | title: title,
29 | leading: IconTheme(
30 | data: Theme.of(context)
31 | .iconTheme
32 | .copyWith(color: isDark ? Colors.white : Colors.black),
33 | child: leading,
34 | ),
35 | ),
36 | ),
37 | );
38 | }
39 | }
40 |
41 | class EditorDrawer extends StatelessWidget {
42 | final Directory folder;
43 |
44 | const EditorDrawer({Key key, this.folder}) : super(key: key);
45 |
46 | @override
47 | Widget build(BuildContext context) {
48 | final ThemeData _theme = Theme.of(context);
49 | bool isDark = _theme.brightness == Brightness.dark;
50 | Color objectColor = isDark ? Colors.white : Colors.black;
51 | return Theme(
52 | data: Theme.of(context).copyWith(
53 | canvasColor: isDark ? Colors.black87 : null,
54 | iconTheme: Theme.of(context).iconTheme.copyWith(
55 | color: objectColor,
56 | ),
57 | textTheme: Theme.of(context).textTheme.copyWith(
58 | bodyText1: TextStyle(color: objectColor),
59 | ),
60 | ),
61 | child: Drawer(
62 | child: ListView(
63 | children: [
64 | // ListTile(
65 | // title: const Text("Finder"),
66 | // leading: Icon(Icons.find_in_page),
67 | // ),
68 | Tooltip(
69 | message: 'Browse workspace',
70 | child: ListTile(
71 | title: const Text("Explorer"),
72 | leading: Icon(
73 | FluentIcons.compass_northwest_20_regular,
74 | ),
75 | onTap: () async {
76 | await Navigator.of(context).pushNamed(
77 | WorkspaceExplorerScreenRoute,
78 | arguments: folder,
79 | );
80 | await Navigator.of(context).pop();
81 | },
82 | ),
83 | ),
84 | // ListTile(
85 | // title: const Text("Search"),
86 | // leading: Icon(
87 | // FluentIcons.search_20_regular,
88 | // ),
89 | // ),
90 | // ListTile(
91 | // title: const Text("Source control"),
92 | // leading: Icon(Icons.timeline),
93 | // ),
94 | // ListTile(
95 | // title: const Text("Run"),
96 | // leading: Icon(
97 | // FluentIcons.arrow_right_circle_24_regular,
98 | // ),
99 | // ),
100 | ListTile(
101 | title: const Text("Terminal"),
102 | leading: Icon(
103 | FluentIcons.code_20_regular,
104 | ),
105 | onTap: () {
106 | Navigator.pushNamed(context, TerminalScreenRoute);
107 | },
108 | ),
109 | ColoredListTile(
110 | isDark,
111 | Colors.grey,
112 | () {
113 | Navigator.of(context).popAndPushNamed(SettingsScreenRoute);
114 | },
115 | const Text("Settings"),
116 | Icon(
117 | FluentIcons.settings_20_regular,
118 | ),
119 | ),
120 | ColoredListTile(
121 | isDark,
122 | Colors.red,
123 | () {
124 | Navigator.of(context).pushReplacementNamed(StartScreenRoute);
125 | },
126 | const Text("Close"),
127 | Icon(
128 | FluentIcons.dismiss_circle_20_regular,
129 | ),
130 | ),
131 | ],
132 | ),
133 | ),
134 | );
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/packages/creamy_field/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "2.8.2"
11 | boolean_selector:
12 | dependency: transitive
13 | description:
14 | name: boolean_selector
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "2.1.0"
18 | characters:
19 | dependency: transitive
20 | description:
21 | name: characters
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "1.2.0"
25 | charcode:
26 | dependency: transitive
27 | description:
28 | name: charcode
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "1.3.1"
32 | clock:
33 | dependency: transitive
34 | description:
35 | name: clock
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "1.1.0"
39 | collection:
40 | dependency: transitive
41 | description:
42 | name: collection
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "1.15.0"
46 | fake_async:
47 | dependency: transitive
48 | description:
49 | name: fake_async
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "1.2.0"
53 | flutter:
54 | dependency: "direct main"
55 | description: flutter
56 | source: sdk
57 | version: "0.0.0"
58 | flutter_highlighter:
59 | dependency: "direct main"
60 | description:
61 | name: flutter_highlighter
62 | url: "https://pub.dartlang.org"
63 | source: hosted
64 | version: "0.1.1"
65 | flutter_lints:
66 | dependency: "direct dev"
67 | description:
68 | name: flutter_lints
69 | url: "https://pub.dartlang.org"
70 | source: hosted
71 | version: "1.0.4"
72 | flutter_test:
73 | dependency: "direct dev"
74 | description: flutter
75 | source: sdk
76 | version: "0.0.0"
77 | highlighter:
78 | dependency: "direct main"
79 | description:
80 | name: highlighter
81 | url: "https://pub.dartlang.org"
82 | source: hosted
83 | version: "0.1.1"
84 | lints:
85 | dependency: transitive
86 | description:
87 | name: lints
88 | url: "https://pub.dartlang.org"
89 | source: hosted
90 | version: "1.0.1"
91 | matcher:
92 | dependency: transitive
93 | description:
94 | name: matcher
95 | url: "https://pub.dartlang.org"
96 | source: hosted
97 | version: "0.12.11"
98 | meta:
99 | dependency: transitive
100 | description:
101 | name: meta
102 | url: "https://pub.dartlang.org"
103 | source: hosted
104 | version: "1.7.0"
105 | path:
106 | dependency: transitive
107 | description:
108 | name: path
109 | url: "https://pub.dartlang.org"
110 | source: hosted
111 | version: "1.8.0"
112 | sky_engine:
113 | dependency: transitive
114 | description: flutter
115 | source: sdk
116 | version: "0.0.99"
117 | source_span:
118 | dependency: transitive
119 | description:
120 | name: source_span
121 | url: "https://pub.dartlang.org"
122 | source: hosted
123 | version: "1.8.1"
124 | stack_trace:
125 | dependency: transitive
126 | description:
127 | name: stack_trace
128 | url: "https://pub.dartlang.org"
129 | source: hosted
130 | version: "1.10.0"
131 | stream_channel:
132 | dependency: transitive
133 | description:
134 | name: stream_channel
135 | url: "https://pub.dartlang.org"
136 | source: hosted
137 | version: "2.1.0"
138 | string_scanner:
139 | dependency: transitive
140 | description:
141 | name: string_scanner
142 | url: "https://pub.dartlang.org"
143 | source: hosted
144 | version: "1.1.0"
145 | term_glyph:
146 | dependency: transitive
147 | description:
148 | name: term_glyph
149 | url: "https://pub.dartlang.org"
150 | source: hosted
151 | version: "1.2.0"
152 | test_api:
153 | dependency: transitive
154 | description:
155 | name: test_api
156 | url: "https://pub.dartlang.org"
157 | source: hosted
158 | version: "0.4.3"
159 | typed_data:
160 | dependency: transitive
161 | description:
162 | name: typed_data
163 | url: "https://pub.dartlang.org"
164 | source: hosted
165 | version: "1.3.0"
166 | vector_math:
167 | dependency: transitive
168 | description:
169 | name: vector_math
170 | url: "https://pub.dartlang.org"
171 | source: hosted
172 | version: "2.1.1"
173 | sdks:
174 | dart: ">=2.15.0 <3.0.0"
175 | flutter: ">=2.8.0"
176 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/assets/fonts/Source_Code_Pro/OFL.txt:
--------------------------------------------------------------------------------
1 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
2 |
3 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
4 | This license is copied below, and is also available with a FAQ at:
5 | http://scripts.sil.org/OFL
6 |
7 |
8 | -----------------------------------------------------------
9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10 | -----------------------------------------------------------
11 |
12 | PREAMBLE
13 | The goals of the Open Font License (OFL) are to stimulate worldwide
14 | development of collaborative font projects, to support the font creation
15 | efforts of academic and linguistic communities, and to provide a free and
16 | open framework in which fonts may be shared and improved in partnership
17 | with others.
18 |
19 | The OFL allows the licensed fonts to be used, studied, modified and
20 | redistributed freely as long as they are not sold by themselves. The
21 | fonts, including any derivative works, can be bundled, embedded,
22 | redistributed and/or sold with any software provided that any reserved
23 | names are not used by derivative works. The fonts and derivatives,
24 | however, cannot be released under any other type of license. The
25 | requirement for fonts to remain under this license does not apply
26 | to any document created using the fonts or their derivatives.
27 |
28 | DEFINITIONS
29 | "Font Software" refers to the set of files released by the Copyright
30 | Holder(s) under this license and clearly marked as such. This may
31 | include source files, build scripts and documentation.
32 |
33 | "Reserved Font Name" refers to any names specified as such after the
34 | copyright statement(s).
35 |
36 | "Original Version" refers to the collection of Font Software components as
37 | distributed by the Copyright Holder(s).
38 |
39 | "Modified Version" refers to any derivative made by adding to, deleting,
40 | or substituting -- in part or in whole -- any of the components of the
41 | Original Version, by changing formats or by porting the Font Software to a
42 | new environment.
43 |
44 | "Author" refers to any designer, engineer, programmer, technical
45 | writer or other person who contributed to the Font Software.
46 |
47 | PERMISSION & CONDITIONS
48 | Permission is hereby granted, free of charge, to any person obtaining
49 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
50 | redistribute, and sell modified and unmodified copies of the Font
51 | Software, subject to the following conditions:
52 |
53 | 1) Neither the Font Software nor any of its individual components,
54 | in Original or Modified Versions, may be sold by itself.
55 |
56 | 2) Original or Modified Versions of the Font Software may be bundled,
57 | redistributed and/or sold with any software, provided that each copy
58 | contains the above copyright notice and this license. These can be
59 | included either as stand-alone text files, human-readable headers or
60 | in the appropriate machine-readable metadata fields within text or
61 | binary files as long as those fields can be easily viewed by the user.
62 |
63 | 3) No Modified Version of the Font Software may use the Reserved Font
64 | Name(s) unless explicit written permission is granted by the corresponding
65 | Copyright Holder. This restriction only applies to the primary font name as
66 | presented to the users.
67 |
68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69 | Software shall not be used to promote, endorse or advertise any
70 | Modified Version, except to acknowledge the contribution(s) of the
71 | Copyright Holder(s) and the Author(s) or with their explicit written
72 | permission.
73 |
74 | 5) The Font Software, modified or unmodified, in part or in whole,
75 | must be distributed entirely under this license, and must not be
76 | distributed under any other license. The requirement for fonts to
77 | remain under this license does not apply to any document created
78 | using the Font Software.
79 |
80 | TERMINATION
81 | This license becomes null and void if any of the above conditions are
82 | not met.
83 |
84 | DISCLAIMER
85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93 | OTHER DEALINGS IN THE FONT SOFTWARE.
94 |
--------------------------------------------------------------------------------
/packages/creamy_field/example/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "2.8.2"
11 | boolean_selector:
12 | dependency: transitive
13 | description:
14 | name: boolean_selector
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "2.1.0"
18 | characters:
19 | dependency: transitive
20 | description:
21 | name: characters
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "1.2.0"
25 | charcode:
26 | dependency: transitive
27 | description:
28 | name: charcode
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "1.3.1"
32 | clock:
33 | dependency: transitive
34 | description:
35 | name: clock
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "1.1.0"
39 | collection:
40 | dependency: transitive
41 | description:
42 | name: collection
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "1.15.0"
46 | creamy_field:
47 | dependency: "direct main"
48 | description:
49 | path: ".."
50 | relative: true
51 | source: path
52 | version: "0.4.1"
53 | cupertino_icons:
54 | dependency: "direct main"
55 | description:
56 | name: cupertino_icons
57 | url: "https://pub.dartlang.org"
58 | source: hosted
59 | version: "1.0.2"
60 | fake_async:
61 | dependency: transitive
62 | description:
63 | name: fake_async
64 | url: "https://pub.dartlang.org"
65 | source: hosted
66 | version: "1.2.0"
67 | flutter:
68 | dependency: "direct main"
69 | description: flutter
70 | source: sdk
71 | version: "0.0.0"
72 | flutter_highlighter:
73 | dependency: transitive
74 | description:
75 | name: flutter_highlighter
76 | url: "https://pub.dartlang.org"
77 | source: hosted
78 | version: "0.1.1"
79 | flutter_lints:
80 | dependency: "direct dev"
81 | description:
82 | name: flutter_lints
83 | url: "https://pub.dartlang.org"
84 | source: hosted
85 | version: "1.0.4"
86 | flutter_test:
87 | dependency: "direct dev"
88 | description: flutter
89 | source: sdk
90 | version: "0.0.0"
91 | highlighter:
92 | dependency: transitive
93 | description:
94 | name: highlighter
95 | url: "https://pub.dartlang.org"
96 | source: hosted
97 | version: "0.1.1"
98 | lints:
99 | dependency: transitive
100 | description:
101 | name: lints
102 | url: "https://pub.dartlang.org"
103 | source: hosted
104 | version: "1.0.1"
105 | matcher:
106 | dependency: transitive
107 | description:
108 | name: matcher
109 | url: "https://pub.dartlang.org"
110 | source: hosted
111 | version: "0.12.11"
112 | meta:
113 | dependency: transitive
114 | description:
115 | name: meta
116 | url: "https://pub.dartlang.org"
117 | source: hosted
118 | version: "1.7.0"
119 | path:
120 | dependency: transitive
121 | description:
122 | name: path
123 | url: "https://pub.dartlang.org"
124 | source: hosted
125 | version: "1.8.0"
126 | sky_engine:
127 | dependency: transitive
128 | description: flutter
129 | source: sdk
130 | version: "0.0.99"
131 | source_span:
132 | dependency: transitive
133 | description:
134 | name: source_span
135 | url: "https://pub.dartlang.org"
136 | source: hosted
137 | version: "1.8.1"
138 | stack_trace:
139 | dependency: transitive
140 | description:
141 | name: stack_trace
142 | url: "https://pub.dartlang.org"
143 | source: hosted
144 | version: "1.10.0"
145 | stream_channel:
146 | dependency: transitive
147 | description:
148 | name: stream_channel
149 | url: "https://pub.dartlang.org"
150 | source: hosted
151 | version: "2.1.0"
152 | string_scanner:
153 | dependency: transitive
154 | description:
155 | name: string_scanner
156 | url: "https://pub.dartlang.org"
157 | source: hosted
158 | version: "1.1.0"
159 | term_glyph:
160 | dependency: transitive
161 | description:
162 | name: term_glyph
163 | url: "https://pub.dartlang.org"
164 | source: hosted
165 | version: "1.2.0"
166 | test_api:
167 | dependency: transitive
168 | description:
169 | name: test_api
170 | url: "https://pub.dartlang.org"
171 | source: hosted
172 | version: "0.4.3"
173 | typed_data:
174 | dependency: transitive
175 | description:
176 | name: typed_data
177 | url: "https://pub.dartlang.org"
178 | source: hosted
179 | version: "1.3.0"
180 | vector_math:
181 | dependency: transitive
182 | description:
183 | name: vector_math
184 | url: "https://pub.dartlang.org"
185 | source: hosted
186 | version: "2.1.1"
187 | sdks:
188 | dart: ">=2.15.0 <3.0.0"
189 | flutter: ">=2.8.0"
190 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: code
2 | description: Snake code editor
3 |
4 | # The following line prevents the package from being accidentally published to
5 | # pub.dev using `pub publish`. This is preferred for private packages.
6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev
7 |
8 | # The following defines the version and build number for your application.
9 | # A version number is three numbers separated by dots, like 1.2.43
10 | # followed by an optional build number separated by a +.
11 | # Both the version and the builder number may be overridden in flutter
12 | # build by specifying --build-name and --build-number, respectively.
13 | # In Android, build-name is used as versionName while build-number used as versionCode.
14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
16 | # Read more about iOS versioning at
17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
18 | version: 0.5.1-alpha+210410
19 |
20 | environment:
21 | sdk: ">=2.7.0 <3.0.0"
22 |
23 | dependencies:
24 | flutter:
25 | sdk: flutter
26 |
27 | provider: ^5.0.0
28 | fluentui_system_icons: ^1.1.104
29 | eva_icons_flutter: ^3.0.0
30 | shared_preferences: ^2.0.3
31 | path_provider: ^2.0.1
32 | path: ^1.8.0
33 | permission_handler: ^6.0.1
34 | hive: ^2.0.0
35 | hive_flutter: ^1.0.0
36 | creamy_field: ^0.4.0
37 | crypto: ^3.0.0
38 | logging: ^1.0.0
39 | google_fonts: ^2.0.0
40 | file_picker: ^3.0.0
41 | equatable: ^2.0.0
42 | share: ^2.0.1
43 |
44 | dev_dependencies:
45 | lint: ^1.5.3
46 | pedantic: ^1.11.0
47 | flutter_test:
48 | sdk: flutter
49 | # hive_generator fails version resolving
50 | # hive_generator: ^1.0.0
51 | build_runner: ^1.12.2
52 |
53 | dependency_overrides:
54 | creamy_field:
55 | path: ../packages/creamy_field/
56 |
57 | # For information on the generic Dart part of this file, see the
58 | # following page: https://dart.dev/tools/pub/pubspec
59 |
60 | # The following section is specific to Flutter.
61 | flutter:
62 |
63 | # The following line ensures that the Material Icons font is
64 | # included with your application, so that you can use the icons in
65 | # the material Icons class.
66 | uses-material-design: true
67 | fonts:
68 | - family: SourceCodePro
69 | fonts:
70 | - asset: assets/fonts/Source_Code_Pro/SourceCodePro-ExtraLight.ttf
71 | weight: 100
72 | - asset: assets/fonts/Source_Code_Pro/SourceCodePro-ExtraLightItalic.ttf
73 | weight: 100
74 | style: italic
75 | - asset: assets/fonts/Source_Code_Pro/SourceCodePro-Light.ttf
76 | weight: 300
77 | - asset: assets/fonts/Source_Code_Pro/SourceCodePro-LightItalic.ttf
78 | weight: 300
79 | style: italic
80 | - asset: assets/fonts/Source_Code_Pro/SourceCodePro-Regular.ttf
81 | weight: 400
82 | - asset: assets/fonts/Source_Code_Pro/SourceCodePro-Italic.ttf
83 | weight: 400
84 | style: italic
85 | - asset: assets/fonts/Source_Code_Pro/SourceCodePro-Medium.ttf
86 | weight: 500
87 | - asset: assets/fonts/Source_Code_Pro/SourceCodePro-MediumItalic.ttf
88 | weight: 500
89 | style: italic
90 | - asset: assets/fonts/Source_Code_Pro/SourceCodePro-SemiBold.ttf
91 | weight: 700
92 | - asset: assets/fonts/Source_Code_Pro/SourceCodePro-SemiBoldItalic.ttf
93 | weight: 700
94 | style: italic
95 | - asset: assets/fonts/Source_Code_Pro/SourceCodePro-Bold.ttf
96 | weight: 800
97 | - asset: assets/fonts/Source_Code_Pro/SourceCodePro-BoldItalic.ttf
98 | weight: 800
99 | style: italic
100 | - asset: assets/fonts/Source_Code_Pro/SourceCodePro-Black.ttf
101 | weight: 900
102 | - asset: assets/fonts/Source_Code_Pro/SourceCodePro-BlackItalic.ttf
103 | weight: 900
104 | style: italic
105 | # To add assets to your application, add an assets section, like this:
106 | # assets:
107 | # - images/a_dot_burr.jpeg
108 | # - images/a_dot_ham.jpeg
109 |
110 | # An image asset can refer to one or more resolution-specific "variants", see
111 | # https://flutter.dev/assets-and-images/#resolution-aware.
112 |
113 | # For details regarding adding assets from package dependencies, see
114 | # https://flutter.dev/assets-and-images/#from-packages
115 |
116 | # To add custom fonts to your application, add a fonts section here,
117 | # in this "flutter" section. Each entry in this list should have a
118 | # "family" key with the font family name, and a "fonts" key with a
119 | # list giving the asset and other descriptors for the font. For
120 | # example:
121 | # fonts:
122 | # - family: Schyler
123 | # fonts:
124 | # - asset: fonts/Schyler-Regular.ttf
125 | # - asset: fonts/Schyler-Italic.ttf
126 | # style: italic
127 | # - family: Trajan Pro
128 | # fonts:
129 | # - asset: fonts/TrajanPro.ttf
130 | # - asset: fonts/TrajanPro_Bold.ttf
131 | # weight: 700
132 | #
133 | # For details regarding fonts from package dependencies,
134 | # see https://flutter.dev/custom-fonts/#from-packages
135 |
--------------------------------------------------------------------------------
/packages/creamy_field/test/creamy_field_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:creamy_field/creamy_field.dart';
2 | import 'package:flutter/services.dart';
3 | import 'package:flutter_test/flutter_test.dart';
4 |
5 | const String initialText =
6 | """Lorem ipsum is typically a corrupted version of De finibus bonorum et malorum;
7 | A first-century BC text by the Roman statesman and philosopher Cicero, with words altered, added, and removed to make it nonsensical, improper Latin.
8 |
9 | Versions of the Lorem ipsum text have been used in typesetting at least since the 1960s, when it was popularized by advertisements for Letraset transfer sheets.
10 | Lorem ipsum was introduced to the digital world in the mid-1980s when Aldus employed it in graphic and word-processing templates for its desktop publishing program PageMaker. Other popular word processors including Pages and Microsoft Word have since adopted Lorem ipsum as well. """;
11 |
12 | void main() {
13 | group('Tests for descriptions obtained from CreamyEditingController: ', () {
14 | test('Text description test - 1', () {
15 | const int _baseOffset = 0, _extentOffset = 11;
16 | final CreamyEditingController textController1 =
17 | CreamyEditingController.fromValue(
18 | TextEditingValue(
19 | text: initialText,
20 | selection: const TextSelection(
21 | baseOffset: _baseOffset,
22 | extentOffset: _extentOffset,
23 | ),
24 | ),
25 | );
26 | expect(textController1.selectedText,
27 | initialText.substring(_baseOffset, _extentOffset));
28 | expect(textController1.totalLineCount, 5);
29 | expect(textController1.atLine, 1);
30 | expect(textController1.atColumn, 12);
31 | expect(textController1.baseColumn, 1);
32 | expect(textController1.extentColumn, 12);
33 | });
34 |
35 | test('Text description test - 2', () {
36 | const int _baseOffset = 79, _extentOffset = 94;
37 | final CreamyEditingController textController2 =
38 | CreamyEditingController.fromValue(
39 | TextEditingValue(
40 | text: initialText,
41 | selection: const TextSelection(
42 | baseOffset: _baseOffset,
43 | extentOffset: _extentOffset,
44 | ),
45 | ),
46 | );
47 | expect(textController2.selectedText,
48 | initialText.substring(_baseOffset, _extentOffset));
49 | expect(textController2.totalLineCount, 5);
50 | expect(textController2.atLine, 2);
51 | expect(textController2.atColumn, 16);
52 | expect(textController2.baseColumn, 1);
53 | expect(textController2.extentColumn, 16);
54 | });
55 |
56 | test('Text description test - 3', () {
57 | int _baseOffset = initialText.length - 7,
58 | _extentOffset = initialText.length;
59 | final CreamyEditingController textController3 =
60 | CreamyEditingController.fromValue(
61 | TextEditingValue(
62 | text: initialText,
63 | selection: TextSelection(
64 | baseOffset: _baseOffset,
65 | extentOffset: _extentOffset,
66 | ),
67 | ),
68 | );
69 | expect(textController3.selectedText,
70 | initialText.substring(_baseOffset, _extentOffset));
71 | expect(textController3.totalLineCount, 5);
72 | expect(textController3.atLine, 5);
73 | expect(textController3.atColumn, 281);
74 | expect(textController3.baseColumn, 274);
75 | expect(textController3.extentColumn, 281);
76 | });
77 | });
78 |
79 | group(
80 | 'When using CreamyEditingController, are \\t getting replaced with spaces when',
81 | () {
82 | late CreamyEditingController controller;
83 | final int tabSize = 4;
84 | final String textWithSpaces = 'hello${' ' * tabSize}world';
85 | final String textWithTabs = 'hello\tworld';
86 | setUp(() {
87 | controller = CreamyEditingController(
88 | text: textWithTabs,
89 | tabSize: tabSize,
90 | );
91 | controller.selection = TextSelection.fromPosition(
92 | TextPosition(offset: controller.text.length));
93 | });
94 | test('the constructor is initialized with a text containing `\\t`', () {
95 | expect(
96 | controller.text,
97 | textWithSpaces,
98 | reason:
99 | 'The number of spaces replaced by single tab is not equal to tabSize',
100 | );
101 | });
102 | test('text is changed', () {
103 | final String newLineWithTab = '\n\tAdded tab on start of this line';
104 | final String newLineWithSpaces =
105 | '\n${' ' * tabSize}Added tab on start of this line';
106 | controller.text = '${controller.text}$newLineWithTab';
107 | expect(controller.text, textWithSpaces + newLineWithSpaces);
108 | });
109 |
110 | test('\\t is added to the end', () {
111 | final String previousText = controller.text;
112 | controller.addTab();
113 | final int newBaseOffset = controller.selection.baseOffset;
114 | final String newText = controller.text;
115 | expect(newBaseOffset, controller.text.length,
116 | reason: 'cursor was not moved to the end');
117 | expect(newText, previousText + (' ' * tabSize),
118 | reason:
119 | 'At the end, the number of spaces replaced by single tab is not equal to tabSize');
120 | });
121 |
122 | test('\\t is added not in the end or start', () {
123 | final String previousText = controller.text;
124 | final int offset = (previousText.length / 2).round();
125 | controller.selection = TextSelection.fromPosition(TextPosition(
126 | offset: offset,
127 | ));
128 | final int oldBaseOffset = controller.selection.baseOffset;
129 | controller.addTab();
130 | final int newBaseOffset = controller.selection.baseOffset;
131 | final String newText = controller.text;
132 | expect(newBaseOffset, oldBaseOffset + tabSize,
133 | reason:
134 | 'cursor was not moved to the correct position after tabs where replaced with spaces, previous offset: ${previousText.length}, tabSize: $tabSize');
135 | expect(
136 | newText,
137 | previousText.substring(0, offset) +
138 | (' ' * tabSize) +
139 | previousText.substring(offset, previousText.length),
140 | reason:
141 | 'At the end, the number of spaces replaced by single tab is not equal to tabSize');
142 | });
143 | tearDown(() {
144 | controller.dispose();
145 | });
146 | });
147 | }
148 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/ui/components/start_tips/tips.dart:
--------------------------------------------------------------------------------
1 | import 'package:eva_icons_flutter/eva_icons_flutter.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | import 'dart:math';
5 |
6 | import 'package:provider/provider.dart';
7 |
8 | class StartTipsController with ChangeNotifier {
9 | StartScreenController() {
10 | _generateRandomIndexForTip();
11 | }
12 |
13 | // TIPS
14 |
15 | bool _showTips = true;
16 |
17 | bool get showTips => _showTips ?? false;
18 |
19 | int _tipIndex = 0;
20 |
21 | void _generateRandomIndexForTip() {
22 | final DateTime time = DateTime.now();
23 | final Random rand = Random(time.millisecond);
24 | _tipIndex = rand.nextInt(_tips.length);
25 | }
26 |
27 | void expandTips() {
28 | _showTips = true;
29 | notifyListeners();
30 | }
31 |
32 | void collapseTips() {
33 | _showTips = false;
34 | notifyListeners();
35 | }
36 |
37 | int getTipIndex() {
38 | return _tipIndex;
39 | }
40 |
41 | void nextTip() {
42 | final _potentialIndex = _tipIndex + 1;
43 | _tipIndex = _potentialIndex == _tips.length ? 0 : _potentialIndex;
44 | notifyListeners();
45 | }
46 |
47 | void previousTip() {
48 | final _potentialIndex = _tipIndex - 1;
49 | _tipIndex = _potentialIndex == -1 ? (_tips.length - 1) : _potentialIndex;
50 | notifyListeners();
51 | }
52 |
53 | final List _tips = [
54 | 'Files are good for writing code snippets, general programs & short scripts. Opening a directory as a project will allow working with multiple files.',
55 | 'You can open directory from Termux app storage if you tap on import while keeping Termux app open in background!',
56 | 'Your duration of work in a folder or on a file with last modified time will be used to offer suggestions later'
57 | ];
58 |
59 | List get tips => _tips ?? [];
60 | }
61 |
62 | class StartTips extends StatelessWidget {
63 | @override
64 | Widget build(BuildContext context) {
65 | final _theme = Theme.of(context);
66 | final _isDarkMode = _theme.brightness == Brightness.dark;
67 | final _borderRadius = BorderRadius.circular(10);
68 | final foregroundColorOnDarkBackground =
69 | _isDarkMode ? Colors.white.withOpacity(1) : Color(0xEE212121);
70 | return Padding(
71 | padding: const EdgeInsets.only(left: 8, bottom: 16),
72 | child: Consumer(
73 | builder: (context, value, child) {
74 | if (!value.showTips) {
75 | return Padding(
76 | padding: const EdgeInsets.symmetric(horizontal: 8.0),
77 | child: Flex(
78 | direction: Axis.horizontal,
79 | children: [
80 | Material(
81 | borderRadius: _borderRadius,
82 | elevation: _isDarkMode ? 0 : 2,
83 | child: InkWell(
84 | onTap: value.expandTips,
85 | borderRadius: _borderRadius,
86 | child: Padding(
87 | padding: const EdgeInsets.symmetric(
88 | horizontal: 8.0, vertical: 4),
89 | child: Icon(EvaIcons.moreHorizontalOutline),
90 | ),
91 | ),
92 | ),
93 | ],
94 | ),
95 | );
96 | }
97 | final int _index = value.getTipIndex();
98 | return Padding(
99 | padding: const EdgeInsets.symmetric(horizontal: 8.0),
100 | child: Material(
101 | borderRadius: _borderRadius,
102 | elevation: _isDarkMode ? 0 : 2,
103 | child: Padding(
104 | padding:
105 | const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4),
106 | child: Stack(
107 | children: [
108 | Column(
109 | children: [
110 | Padding(
111 | padding: const EdgeInsets.only(left: 8.0),
112 | child: Row(
113 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
114 | children: [
115 | Text(
116 | 'TIP #${_index + 1}',
117 | style: _theme.textTheme.subtitle1.copyWith(
118 | color: foregroundColorOnDarkBackground,
119 | ),
120 | ),
121 | IconButton(
122 | padding: const EdgeInsets.all(0),
123 | icon: Icon(EvaIcons.close),
124 | color: foregroundColorOnDarkBackground,
125 | onPressed: value.collapseTips,
126 | ),
127 | ],
128 | ),
129 | ),
130 | Padding(
131 | padding: const EdgeInsets.only(left: 8, right: 8),
132 | child: Text(
133 | value.tips[_index],
134 | style: _theme.textTheme.subtitle2.copyWith(
135 | color: foregroundColorOnDarkBackground,
136 | ),
137 | ),
138 | ),
139 | Padding(
140 | padding: const EdgeInsets.only(left: 8.0, bottom: 8),
141 | child: Row(
142 | mainAxisAlignment: MainAxisAlignment.end,
143 | children: [
144 | IconButton(
145 | icon: Icon(EvaIcons.chevronLeftOutline),
146 | color: foregroundColorOnDarkBackground,
147 | onPressed: value.previousTip,
148 | ),
149 | IconButton(
150 | icon: Icon(EvaIcons.chevronRightOutline),
151 | color: foregroundColorOnDarkBackground,
152 | onPressed: value.nextTip,
153 | ),
154 | ],
155 | ),
156 | ),
157 | ],
158 | ),
159 | // Close button above the tip
160 | ],
161 | ),
162 | ),
163 | ),
164 | );
165 | },
166 | ),
167 | );
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/packages/creamy_field/lib/src/text_tools/toolbar_options.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/foundation.dart';
2 | import 'package:flutter/rendering.dart' show TextSelectionPoint;
3 | import 'package:flutter/widgets.dart';
4 |
5 | import '../../creamy_field.dart';
6 |
7 | // Intermediate data used for building menu items with the _getItems method.
8 | class CreamyTextSelectionToolbarAction {
9 | final String label;
10 | final bool visible;
11 | final void Function() onPressed;
12 |
13 | const CreamyTextSelectionToolbarAction({
14 | required this.label,
15 | required this.onPressed,
16 | this.visible = true,
17 | });
18 | }
19 |
20 | enum TextSelectionControlsType {
21 | material,
22 | }
23 |
24 | /// Normally, when a [TextSelectionControls] calls [buildToolbar], the UI for the toolbar is painted.
25 | /// By default, this doesn't support additional actions other than copy, cut, select all & paste.
26 | mixin CreamyTextSelectionControls implements TextSelectionControls {
27 | /// Builds a toolbar without additional actions
28 | @nonVirtual
29 | @override
30 | Widget buildToolbar(
31 | BuildContext context,
32 | Rect globalEditableRegion,
33 | double textLineHeight,
34 | Offset selectionMidpoint,
35 | List endpoints,
36 | TextSelectionDelegate delegate,
37 | ClipboardStatusNotifier clipboardStatus,
38 | Offset? lastSecondaryTapDownPosition,
39 | ) {
40 | return buildToolbarWithActions(
41 | context,
42 | globalEditableRegion,
43 | textLineHeight,
44 | selectionMidpoint,
45 | endpoints,
46 | delegate,
47 | clipboardStatus,
48 | lastSecondaryTapDownPosition,
49 | );
50 | }
51 |
52 | /// Build a toolbar which supports actions.
53 | ///
54 | /// [CreamyTextSelectionControlsProvider] uses this to create toolbar with actions.
55 | ///
56 | /// This callback is also called by the [buildToolbar] to create a toolbar without actions.
57 | Widget buildToolbarWithActions(
58 | BuildContext context,
59 | Rect globalEditableRegion,
60 | double textLineHeight,
61 | Offset position,
62 | List endpoints,
63 | TextSelectionDelegate delegate,
64 | ClipboardStatusNotifier clipboardStatus,
65 | Offset? lastSecondaryTapDownPosition, {
66 | List actions,
67 | });
68 | }
69 |
70 | typedef List ActionsBuilderCallback(
71 | BuildContext context,
72 | TextSelectionDelegate delegate,
73 | ClipboardStatusNotifier clipboardStatus,
74 | );
75 |
76 | /// A [TextSelectionControls] provider with actions.
77 | ///
78 | /// Provide additional options to the text selection toolbar.
79 | ///
80 | /// This delegates methods from [controls] to allow compatiblity with Material Text input fields.
81 | class CreamyTextSelectionControlsProvider implements TextSelectionControls {
82 | final CreamyTextSelectionControls controls;
83 |
84 | /// More actions the selection toolbar should offer.
85 | ActionsBuilderCallback actionsBuilder;
86 |
87 | /// A Provider of a custom [CreamyTextSelectionControls] with actions.
88 | CreamyTextSelectionControlsProvider.custom({
89 | required this.controls,
90 | required this.actionsBuilder,
91 | });
92 |
93 | /// Provide text selection controls from this package,
94 | ///
95 | /// Describe the type of selections controls with [type].
96 | /// Use [actionsBuilder] to build actions for the selection controls.
97 | factory CreamyTextSelectionControlsProvider({
98 | required TextSelectionControlsType type,
99 | required ActionsBuilderCallback actionsBuilder,
100 | }) {
101 | TextSelectionControls textSelectionControls;
102 |
103 | switch (type) {
104 | case TextSelectionControlsType.material:
105 | default:
106 | textSelectionControls = creamyMaterialTextSelectionControls;
107 | }
108 |
109 | return CreamyTextSelectionControlsProvider.custom(
110 | controls: textSelectionControls as CreamyTextSelectionControls,
111 | actionsBuilder: actionsBuilder,
112 | );
113 | }
114 |
115 | @override
116 | Widget buildHandle(
117 | BuildContext context,
118 | TextSelectionHandleType type,
119 | double textLineHeight, [
120 | VoidCallback? onTap,
121 | double? startGlyphHeight,
122 | double? endGlyphHeight,
123 | ]) =>
124 | controls.buildHandle(
125 | context,
126 | type,
127 | textLineHeight,
128 | onTap,
129 | startGlyphHeight,
130 | endGlyphHeight,
131 | );
132 |
133 | @override
134 | Widget buildToolbar(
135 | BuildContext context,
136 | Rect globalEditableRegion,
137 | double textLineHeight,
138 | Offset position,
139 | List endpoints,
140 | TextSelectionDelegate delegate,
141 | ClipboardStatusNotifier clipboardStatus,
142 | Offset? lastSecondaryTapDownPosition) {
143 | return controls.buildToolbarWithActions(
144 | context,
145 | globalEditableRegion,
146 | textLineHeight,
147 | position,
148 | endpoints,
149 | delegate,
150 | clipboardStatus,
151 | lastSecondaryTapDownPosition,
152 | actions: actionsBuilder(
153 | context,
154 | delegate,
155 | clipboardStatus,
156 | ),
157 | );
158 | }
159 |
160 | @override
161 | Offset getHandleAnchor(
162 | TextSelectionHandleType type,
163 | double textLineHeight, [
164 | double? startGlyphHeight,
165 | double? endGlyphHeight,
166 | ]) =>
167 | controls.getHandleAnchor(
168 | type,
169 | textLineHeight,
170 | startGlyphHeight,
171 | endGlyphHeight,
172 | );
173 |
174 | @override
175 | Size getHandleSize(double textLineHeight) => controls.getHandleSize(
176 | textLineHeight,
177 | );
178 |
179 | @override
180 | bool canCopy(TextSelectionDelegate delegate) => controls.canCopy(delegate);
181 |
182 | @override
183 | bool canCut(TextSelectionDelegate delegate) => controls.canCut(delegate);
184 |
185 | @override
186 | bool canPaste(TextSelectionDelegate delegate) => controls.canPaste(delegate);
187 |
188 | @override
189 | bool canSelectAll(TextSelectionDelegate delegate) =>
190 | controls.canSelectAll(delegate);
191 |
192 | @override
193 | void handleCopy(
194 | TextSelectionDelegate delegate,
195 | ClipboardStatusNotifier? clipboardStatus,
196 | ) =>
197 | controls.handleCopy(
198 | delegate,
199 | clipboardStatus,
200 | );
201 |
202 | @override
203 | void handleCut(TextSelectionDelegate delegate,
204 | ClipboardStatusNotifier? clipboardStatus) =>
205 | controls.handleCut(delegate, clipboardStatus);
206 |
207 | @override
208 | Future handlePaste(TextSelectionDelegate delegate) =>
209 | controls.handlePaste(delegate);
210 |
211 | @override
212 | void handleSelectAll(TextSelectionDelegate delegate) =>
213 | controls.handleSelectAll(delegate);
214 | }
215 |
--------------------------------------------------------------------------------
/org.purplegraphite.code/lib/src/ui/components/about/about.dart:
--------------------------------------------------------------------------------
1 | import 'package:code/src/common/ui.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:eva_icons_flutter/eva_icons_flutter.dart';
4 |
5 | import '../../../common/strings.dart';
6 |
7 | void showMyAboutDialog({
8 | @required BuildContext context,
9 | String applicationName,
10 | String applicationVersion,
11 | Widget applicationIcon,
12 | String applicationLegalese,
13 | List children,
14 | bool useRootNavigator = true,
15 | RouteSettings routeSettings,
16 | }) {
17 | assert(context != null);
18 | assert(useRootNavigator != null);
19 | showDialog(
20 | context: context,
21 | useRootNavigator: useRootNavigator,
22 | builder: (BuildContext context) {
23 | return AboutDialog(
24 | applicationVersion: applicationVersion,
25 | applicationIcon: applicationIcon,
26 | applicationLegalese: applicationLegalese,
27 | children: children,
28 | );
29 | },
30 | routeSettings: routeSettings,
31 | );
32 | }
33 |
34 | class AboutDialog extends StatelessWidget {
35 | /// Creates an about box.
36 | ///
37 | /// The arguments are all optional. The application name, if omitted, will be
38 | /// derived from the nearest [Title] widget. The version, icon, and legalese
39 | /// values default to the empty string.
40 | const AboutDialog({
41 | Key key,
42 | this.applicationVersion,
43 | this.applicationIcon,
44 | this.applicationLegalese,
45 | this.children,
46 | }) : super(key: key);
47 |
48 | /// The version of this build of the application.
49 | ///
50 | /// This string is shown under the application name.
51 | ///
52 | /// Defaults to the empty string.
53 | final String applicationVersion;
54 |
55 | /// The icon to show next to the application name.
56 | ///
57 | /// By default no icon is shown.
58 | ///
59 | /// Typically this will be an [ImageIcon] widget. It should honor the
60 | /// [IconTheme]'s [IconThemeData.size].
61 | final Widget applicationIcon;
62 |
63 | /// A string to show in small print.
64 | ///
65 | /// Typically this is a copyright notice.
66 | ///
67 | /// Defaults to the empty string.
68 | final String applicationLegalese;
69 |
70 | /// Widgets to add to the dialog box after the name, version, and legalese.
71 | ///
72 | /// This could include a link to a Web site, some descriptive text, credits,
73 | /// or other information to show in the about box.
74 | ///
75 | /// Defaults to nothing.
76 | final List children;
77 |
78 | @override
79 | Widget build(BuildContext context) {
80 | // final String name = Strings.title;
81 | final String version = applicationVersion;
82 | final Widget icon = applicationIcon;
83 | final TextStyle _outlineButtonTextStyle =
84 | Theme.of(context).textTheme.button.copyWith(
85 | fontSize: 12,
86 | );
87 | // TODO(predatorx7) check AlertDialog's scrollable true
88 | return Dialog(
89 | // backgroundColor: Colors.lightGreen[100],
90 | shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
91 | clipBehavior: Clip.hardEdge,
92 | child: Column(
93 | mainAxisSize: MainAxisSize.min,
94 | crossAxisAlignment: CrossAxisAlignment.stretch,
95 | children: [
96 | Row(
97 | children: [
98 | IconButton(
99 | icon: Icon(EvaIcons.close),
100 | tooltip: 'close',
101 | onPressed: () => Navigator.of(context).maybePop(),
102 | )
103 | ],
104 | ),
105 | Padding(
106 | padding: const EdgeInsets.fromLTRB(24, 0, 24, 24),
107 | child: ListBody(
108 | children: [
109 | if (icon != null)
110 | IconTheme(data: Theme.of(context).iconTheme, child: icon),
111 | Padding(
112 | padding: const EdgeInsets.only(top: 4.0, bottom: 8),
113 | child: RichText(
114 | text: TextSpan(
115 | text: 'version ',
116 | style: Theme.of(context).textTheme.bodyText2.copyWith(
117 | fontWeight: FontWeight.bold,
118 | fontStyle: FontStyle.italic,
119 | color: Colors.green),
120 | children: [
121 | TextSpan(
122 | text: version,
123 | style: Theme.of(context).textTheme.bodyText2.copyWith(
124 | fontStyle: FontStyle.italic,
125 | ),
126 | ),
127 | ],
128 | ),
129 | ),
130 | ),
131 | ...?children,
132 | SizedBox(
133 | height: 10,
134 | ),
135 | // Padding(
136 | // padding: const EdgeInsets.only(top: 10.0, bottom: 5),
137 | // child: ListBody(
138 | // children: [
139 | // Container(height: 18.0),
140 | // Text(
141 | // Strings.applicationLegalese ?? '',
142 | // style: Theme.of(context).textTheme.caption,
143 | // textAlign: TextAlign.left,
144 | // ),
145 | // ],
146 | // ),
147 | // ),
148 | Tooltip(
149 | message: 'Contact us',
150 | child: OutlineButton(
151 | onPressed: () {
152 | print('Contact us');
153 | },
154 | child: Text(
155 | Strings.contactUs,
156 | style: _outlineButtonTextStyle,
157 | ),
158 | shape: Corners.outlinedShapeBorder),
159 | ),
160 | Tooltip(
161 | message: 'View licenses',
162 | child: OutlineButton(
163 | child: Text(
164 | Strings.viewLicenses,
165 | style: _outlineButtonTextStyle,
166 | ),
167 | onPressed: () {
168 | showLicensePage(
169 | context: context,
170 | applicationName: '',
171 | applicationVersion: applicationVersion,
172 | applicationIcon: applicationIcon,
173 | applicationLegalese: applicationLegalese,
174 | );
175 | },
176 | shape: Corners.outlinedShapeBorder,
177 | ),
178 | ),
179 | ],
180 | ),
181 | ),
182 | ],
183 | ),
184 | );
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/packages/creamy_field/lib/src/syntax_highlighter/creamy_syntax_highlighter.dart:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020, Mushaheed Syed. All rights reserved.
2 | //
3 | // Use of this source code is governed by a BSD 3-Clause License that can be
4 | // found in the LICENSE file.
5 | // --------------------------------------------------------------------------------
6 | // MIT License
7 | //
8 | // Copyright (c) 2019 Rongjian Zhang
9 | //
10 | // Permission is hereby granted, free of charge, to any person obtaining a copy
11 | // of this software and associated documentation files (the "Software"), to deal
12 | // in the Software without restriction, including without limitation the rights
13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | // copies of the Software, and to permit persons to whom the Software is
15 | // furnished to do so, subject to the following conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be included in all
18 | // copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 | // SOFTWARE.
27 |
28 | import 'package:flutter/material.dart';
29 | import 'package:flutter/widgets.dart';
30 | import 'package:flutter_highlighter/themes/dark.dart' as dark_theme_highlight;
31 | import 'package:highlighter/highlighter.dart' show highlight, Node;
32 |
33 | import './highlighted_theme_type.dart';
34 | import './language_type.dart';
35 | import '../syntax_highlighter.dart';
36 |
37 | /// Highlights a source code's syntax based on language type & theme type.
38 | ///
39 | /// This highlighter internally uses [flutter_highlight](https://pub.dev/packages/flutter_highlighter)
40 | /// & [highlight](https://pub.dev/packages/highlighter)
41 | class CreamySyntaxHighlighter implements SyntaxHighlighter {
42 | // The original code to be highlighted
43 | late String _source;
44 |
45 | /// Highlight language
46 | ///
47 | /// If null then no syntax highlighting will be done.
48 | ///
49 | /// [All available languages](https://github.com/Purple-Graphite/highlighter/tree/master/highlighter/lib/languages)
50 | final LanguageType language;
51 |
52 | final String? _language;
53 |
54 | /// Highlight theme
55 | ///
56 | /// Defaults to a light theme.
57 | ///
58 | /// [All available themes](https://github.com/Purple-Graphite/highlighter/blob/master/flutter_highlighter/lib/themes)
59 | final HighlightedThemeType theme;
60 |
61 | Map? _theme;
62 |
63 | /// The dark theme used by this highlighter when [brightness] is [Brightness.dark]
64 | ///
65 | /// Defaults to [dark_theme_highlight.darkTheme].
66 | final HighlightedThemeType? darkTheme;
67 |
68 | Map? _darkTheme;
69 |
70 | /// The theme used by this syntax highlighter.
71 | /// If brightness is dark, [darkTheme] is used and when it's light or null, [theme] is used.
72 | final ThemeMode? themeMode;
73 |
74 | Brightness? _brightness;
75 |
76 | /// The Theme brightness of the syntax highlight
77 | Brightness? get brightness => _brightness;
78 |
79 | /// Provide the context to this highligher. This method will be used to update
80 | /// the brighness based on context in [RichEditableText]
81 | ///
82 | /// If you're not using [CreamyField] or [RichEditableText] then use this
83 | /// to obtain context as it's required to get context.
84 | void withContext(BuildContext context) {
85 | // change brightness
86 | switch (themeMode) {
87 | case ThemeMode.light:
88 | _brightness = Brightness.light;
89 | break;
90 | case ThemeMode.dark:
91 | _brightness = Brightness.dark;
92 | break;
93 | case ThemeMode.system:
94 | default:
95 | _brightness = Theme.of(context).brightness;
96 | break;
97 | }
98 | }
99 |
100 | // The effective theme brighness of this syntax highlighter.
101 | Brightness get _effectiveBrightness => _brightness ?? Brightness.light;
102 |
103 | bool get _isThemeDark => _effectiveBrightness == Brightness.dark;
104 |
105 | Map? get _effectiveTheme {
106 | // Returning the regular theme as dark theme is null.
107 | if (_darkTheme == null) return _theme;
108 | // If theme mode is dark returns a dark theme, else returns the regular theme
109 | return _isThemeDark ? _darkTheme : _theme;
110 | }
111 |
112 | /// Creates a syntax highlighter.
113 | ///
114 | /// You can specify the language mode & theme type with [language], [theme], [darkTheme], [brightness] respectively.
115 | ///
116 | /// For the highlight language, it is recommended to give [language] a value for performance
117 | /// [All available languages](https://github.com/Purple-Graphite/highlight/tree/master/highlighter/lib/languages)
118 | ///
119 | /// The supported highlight themes are
120 | /// [All available themes](https://github.com/Purple-Graphite/highlight/blob/master/flutter_highlighter/lib/themes)
121 | ///
122 | /// syntax will not be highlighted if language is null.
123 | CreamySyntaxHighlighter({
124 | required this.language,
125 | required this.theme,
126 | this.darkTheme,
127 | this.themeMode,
128 | }) : this._language = toLanguageName(language),
129 | this._theme = getHighlightedThemeStyle(theme),
130 | this._darkTheme = darkTheme != null
131 | ? getHighlightedThemeStyle(darkTheme)
132 | : dark_theme_highlight.darkTheme;
133 |
134 | List _convert(List nodes) {
135 | List spans = [];
136 | var currentSpans = spans;
137 | List> stack = [];
138 |
139 | void _traverse(Node node) {
140 | final TextStyle? _all =
141 | _isThemeDark ? TextStyle(color: Colors.white) : null;
142 | if (node.value != null) {
143 | currentSpans.add(node.className == null
144 | ? TextSpan(text: node.value, style: _all)
145 | : TextSpan(
146 | text: node.value, style: _effectiveTheme![node.className!]));
147 | } else if (node.children != null) {
148 | List tmp = [];
149 | currentSpans.add(
150 | TextSpan(children: tmp, style: _effectiveTheme![node.className!]));
151 | stack.add(currentSpans);
152 | currentSpans = tmp;
153 |
154 | node.children!.forEach((n) {
155 | _traverse(n);
156 | if (n == node.children!.last) {
157 | currentSpans = stack.isEmpty ? spans : stack.removeLast();
158 | }
159 | });
160 | }
161 | }
162 |
163 | for (var node in nodes) {
164 | _traverse(node);
165 | }
166 |
167 | return spans;
168 | }
169 |
170 | @override
171 | List parseTextEditingValue(TextEditingValue? value) {
172 | _source = value!.text;
173 | return _convert(highlight
174 | .parse(_source, language: _language, autoDetection: _language == null)
175 | .nodes!);
176 | }
177 | }
178 |
--------------------------------------------------------------------------------