init
This commit is contained in:
commit
acc61e858b
18 changed files with 5068 additions and 0 deletions
7
.sajt/config.yaml
Normal file
7
.sajt/config.yaml
Normal file
|
@ -0,0 +1,7 @@
|
|||
site:
|
||||
title: Artur Gurgul - {{article.title}}
|
||||
remote:
|
||||
type: ssh
|
||||
user: debian
|
||||
host: artur.gurgul.pro
|
||||
path: /var/www/artur.gurgul.pro
|
126
go/notes.md
Normal file
126
go/notes.md
Normal file
|
@ -0,0 +1,126 @@
|
|||
---
|
||||
layout: default
|
||||
title: Golang notes
|
||||
categories: programming
|
||||
---
|
||||
|
||||
```go
|
||||
ch := make(chan int)
|
||||
go func() {
|
||||
ch <- 42
|
||||
}()
|
||||
fmt.Println(<-ch)
|
||||
```
|
||||
|
||||
|
||||
```go
|
||||
type Base struct {
|
||||
ID int
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Base
|
||||
Name string
|
||||
}
|
||||
|
||||
u := User{Base: Base{ID: 1}, Name: "John"}
|
||||
fmt.Println(u.ID, u.Name)
|
||||
```
|
||||
|
||||
```go
|
||||
ch := make(chan int)
|
||||
go func() {
|
||||
ch <- 42
|
||||
}()
|
||||
fmt.Println(<-ch)
|
||||
```
|
||||
|
||||
Custom String Representations:
|
||||
• Implement the Stringer interface for custom string representations of your types
|
||||
|
||||
```go
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
func (p Person) String() string {
|
||||
return fmt.Sprintf("%s is %d years old", p.Name, p.Age)
|
||||
}
|
||||
|
||||
p := Person{Name: "Alice", Age: 30}
|
||||
fmt.Println(p)
|
||||
```
|
||||
|
||||
Use init Function for Initialization:
|
||||
• The init function is called before the main function and is useful for setup tasks.
|
||||
|
||||
```go
|
||||
|
||||
var config Config
|
||||
|
||||
func init() {
|
||||
config = loadConfig()
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println(config)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
```go
|
||||
func add(a, b int) int {
|
||||
return a + b
|
||||
}
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
result := add(2, 3)
|
||||
if result != 5 {
|
||||
t.Errorf("expected 5, got %d", result)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use context for Cancellation and Timeouts:
|
||||
• Use the context package to handle cancellation and timeouts in concurrent operations.
|
||||
|
||||
|
||||
```go
|
||||
func doWork(ctx context.Context) {
|
||||
select {
|
||||
case <-time.After(2 * time.Second):
|
||||
fmt.Println("Work done")
|
||||
case <-ctx.Done():
|
||||
fmt.Println("Canceled")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
go doWork(ctx)
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
```go
|
||||
type Weekday int
|
||||
|
||||
const (
|
||||
Sunday Weekday = iota
|
||||
Monday
|
||||
Tuesday
|
||||
Wednesday
|
||||
Thursday
|
||||
Friday
|
||||
Saturday
|
||||
)
|
||||
|
||||
func (d Weekday) String() string {
|
||||
return [...]string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}[d]
|
||||
}
|
||||
|
||||
fmt.Println(Monday) // Output: Monday
|
||||
```
|
28
index.md
Normal file
28
index.md
Normal file
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
layout: default
|
||||
hidden: true
|
||||
---
|
||||
|
||||
## Welcome to my website!
|
||||
|
||||
This site was generated using **`sajt`**, a project I’m currently developing and looking for contributors to help improve.
|
||||
|
||||
- **Sajt**: [https://github.com/artur-gurgul-pro/sajt](https://github.com/artur-gurgul-pro/sajt)
|
||||
- **This website's source**: [https://github.com/artur-gurgul-pro/notes](https://github.com/artur-gurgul-pro/notes)
|
||||
|
||||
### Currently Supported
|
||||
- Pug
|
||||
- Markdown
|
||||
- HTML
|
||||
|
||||
### Experimental Support
|
||||
- YAML
|
||||
|
||||
### Planned Features
|
||||
- Audio and video file support
|
||||
|
||||
The idea behind **sajt** is simple: turn your notes and content into a website with zero hassle. Just drop your files into a folder, run a command, and you’ve got a website ready to be uploaded to your hosting provider.
|
||||
|
||||
Feel free to share ideas, report bugs, or make suggestions by opening an issue:
|
||||
[https://github.com/artur-gurgul-pro/notes/issues](https://github.com/artur-gurgul-pro/notes/issues)
|
||||
|
658
ios/ios-architecture-patterns.md
Normal file
658
ios/ios-architecture-patterns.md
Normal file
|
@ -0,0 +1,658 @@
|
|||
---
|
||||
layout: default
|
||||
title: iOS Architecture Patterns
|
||||
categories: swift
|
||||
---
|
||||
### Remove story board dependency
|
||||
|
||||
1. Remove `Main.storyboard` file
|
||||
1. Remove storyboard reference from `Info.plist` → In Scene Configuration find `Storyboard Name` and delete it
|
||||
1. Go to build settings and remove `UIKit MainStoryboard File Base Name` field
|
||||
1. Create a window in Scene Delegate
|
||||
|
||||
```swift
|
||||
func scene(_ scene: UIScene,
|
||||
willConnectTo session: UISceneSession,
|
||||
options connectionOptions: UIScene.ConnectionOptions) {
|
||||
|
||||
guard let scene = (scene as? UIWindowScene) else { return }
|
||||
|
||||
let window = UIWindow(windowScene: scene)
|
||||
window.rootViewController = ViewController()
|
||||
window.makeKeyAndVisible()
|
||||
self.window = window
|
||||
}
|
||||
```
|
||||
|
||||
## Dependency injection
|
||||
|
||||
<!-- https://www.swiftbysundell.com/tips/testing-code-that-uses-static-apis/ -->
|
||||
|
||||
[Explanation of the code under this link](https://www.avanderlee.com/swift/dependency-injection/)
|
||||
|
||||
|
||||
```swift
|
||||
public protocol InjectionKey {
|
||||
associatedtype Value
|
||||
static var currentValue: Self.Value { get set }
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
struct InjectedValues {
|
||||
private static var current = InjectedValues()
|
||||
|
||||
static subscript<K>(key: K.Type) -> K.Value where K : InjectionKey {
|
||||
get { key.currentValue }
|
||||
set { key.currentValue = newValue }
|
||||
}
|
||||
|
||||
static subscript<T>(_ keyPath: WritableKeyPath<InjectedValues, T>) -> T {
|
||||
get { current[keyPath: keyPath] }
|
||||
set { current[keyPath: keyPath] = newValue }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
@propertyWrapper
|
||||
struct Injected<T> {
|
||||
private let keyPath: WritableKeyPath<InjectedValues, T>
|
||||
var wrappedValue: T {
|
||||
get { InjectedValues[keyPath] }
|
||||
set { InjectedValues[keyPath] = newValue }
|
||||
}
|
||||
|
||||
init(_ keyPath: WritableKeyPath<InjectedValues, T>) {
|
||||
self.keyPath = keyPath
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Define dependency
|
||||
|
||||
```swift
|
||||
private struct UsersRepositoryKey: InjectionKey {
|
||||
static var currentValue: AnyUsersRepository = UsersRepository()
|
||||
}
|
||||
|
||||
extension InjectedValues {
|
||||
var usersRepository: AnyUsersRepository {
|
||||
get { Self[UsersRepositoryKey.self] }
|
||||
set { Self[UsersRepositoryKey.self] = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
protocol AnyUsersRepository {
|
||||
func getUsers(_ result: @escaping (Result<[User], Error>)->Void)
|
||||
}
|
||||
|
||||
class UsersRepository: AnyUsersRepository {
|
||||
func getUsers(_ result: @escaping (Result<[User], Error>)->Void) {
|
||||
<#Implementation#>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
@Injected(\.usersRepository) var usersRepository: AnyUsersRepository
|
||||
```
|
||||
|
||||
```swift
|
||||
InjectedValues[\.usersRepository] = MockedUsersRepository()
|
||||
```
|
||||
|
||||
## Model-View-Controller
|
||||
|
||||
#### Clasic version
|
||||
|
||||
- `View` and `Model` are linked together, so reusability is reduced.
|
||||
- Note: Views in iOS apps are quite often reusable.
|
||||
|
||||
<p>
|
||||
{% svg ../svgs/classic-mvc.svg class="center-image" %}
|
||||
</p>
|
||||
|
||||
#### Apple version
|
||||
|
||||
<p>
|
||||
{% svg ../svgs/apple-mvc.svg class="center-image" %}
|
||||
</p>
|
||||
|
||||
**Model** responsibilities:
|
||||
|
||||
- Business logic
|
||||
- Accessing and manipulating data
|
||||
- Persistence
|
||||
- Communication/Networking
|
||||
- Parsing
|
||||
- Extensions and helper classes
|
||||
- Communication with models
|
||||
|
||||
Note: The `Model` must not communicate directly with the `View`. The `Controller` is the link between those
|
||||
|
||||
**View** responsibilities:
|
||||
|
||||
- Animations, drawings (`UIView`, `CoreAnimation`, `CoreGraphics`)
|
||||
- Show data that controller sends
|
||||
- Might receive user input
|
||||
|
||||
**Controller** responsibilities:
|
||||
|
||||
- Exchange data between `View` and `Model`
|
||||
- Receive user actions and interruptions or signals from the outside the app
|
||||
- Handles the view life cycle
|
||||
|
||||
#### Advantages
|
||||
|
||||
- Simple and usually less code
|
||||
- Fast development for simple apps
|
||||
|
||||
#### Disadvantages
|
||||
|
||||
- Controllers coupled views
|
||||
- Massive `ViewController`s
|
||||
|
||||
#### Communication between components
|
||||
|
||||
- [Delegation](https://developer.apple.com/library/archive/documentation/General/Conceptual/Devpedia-CocoaApp/TargetAction.html) pattern
|
||||
- [Target-Action](https://developer.apple.com/library/archive/documentation/General/Conceptual/Devpedia-CocoaApp/TargetAction.html) pattern
|
||||
- [Observer](https://developer.apple.com/documentation/foundation/nsnotificationcenter) pattern with `NSNotificationCenter`
|
||||
- [Observer](https://developer.apple.com/documentation/swift/using-key-value-observing-in-swift) pattern with `KVO`
|
||||
|
||||
## Model-View-Presenter
|
||||
|
||||
In this design pattern View is implemented with classes `UIView` and `UIViewController`. The `UIViewController` has less responsibilities which are limited to:
|
||||
|
||||
- Routing/Coordination
|
||||
- Navigation
|
||||
- Passing informations via a delegation pattern
|
||||
|
||||
#### View
|
||||
|
||||
```swift
|
||||
class ExampleController: UIViewController {
|
||||
private let exampleView = ExampleView()
|
||||
override func loadView() {
|
||||
super.loadView()
|
||||
setup()
|
||||
}
|
||||
private func setup() {
|
||||
let presenter = ExamplePresenter(exampleView)
|
||||
exampleView.presenter = presenter
|
||||
exampleView.setupView()
|
||||
self.view = exampleView
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### Presenter
|
||||
|
||||
```swift
|
||||
protocol ExampleViewDelegate {
|
||||
func updateView()
|
||||
}
|
||||
class ExamplePresenter {
|
||||
private weak var exampleView: ExampleViewDelegate?
|
||||
init(_ exampleView: ExampleViewDelegate) {
|
||||
self.exampleView = exampleView
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Advantages
|
||||
|
||||
- Easier to test business logic
|
||||
- Better separation of responsibilities
|
||||
|
||||
#### Disadvantages
|
||||
|
||||
- Usually not a better choice for smaller projects
|
||||
- Presenters might become massive
|
||||
- Controllers still handle navigation. Possible solutions → extend the pattern with Router or Coordinator.
|
||||
|
||||
#### Common layers
|
||||
|
||||
- Data access layer: `CRUD` operations facilitated with `CoreData` `Realm` etc.
|
||||
- Services: Classes that interacts with database entities, like retrieve data, transform them into objects.
|
||||
- Extensions/utils
|
||||
|
||||
## Model-View-ViewModel
|
||||
|
||||
`ViewModel` has no references to the view.
|
||||
|
||||
<p>
|
||||
{% svg ../svgs/mvvm.svg class="center-image" %}
|
||||
</p>
|
||||
|
||||
Binding is done using: `Combine Framework`, `RxSwift`, `Bond` or `KVO` or using delegation pattern
|
||||
|
||||
* **`Model`** does same things as in `MVP` and `MVC`.
|
||||
* **`View`** also is similar, but binds with `ViewModel`
|
||||
* **`ViewModel`** keeps updated state of the view, and process data for i
|
||||
|
||||
#### Advantages
|
||||
|
||||
- Better reparation of responsibilities
|
||||
- Better testability, without needing to take into account the views
|
||||
|
||||
#### Disadvantages
|
||||
|
||||
- Might be slower and introduce dependency on external libraries
|
||||
- Harder to learn and can become complex
|
||||
|
||||
|
||||
#### **Extension with Coordinator MVVM-C**
|
||||
|
||||
Role of `Coordinator` is to manage navigation flow.
|
||||
|
||||
```swift
|
||||
protocol Coordinator {
|
||||
var navigationController: UINavigationController { get set }
|
||||
func start()
|
||||
}
|
||||
```
|
||||
|
||||
and an example implementation
|
||||
|
||||
```swift
|
||||
class ExampleCoordinator: Coordinator {
|
||||
var navigationController: UINavigationController
|
||||
init(navigationController: UINavigationController) {
|
||||
self.navigationController = UINavigationController
|
||||
}
|
||||
|
||||
func start() {
|
||||
let viewModel = ExampleViewModel(someService: SomeService(),
|
||||
coordinator: self)
|
||||
navigationController.pushViewController(ExampleController(viewModel),
|
||||
animated: true)
|
||||
}
|
||||
|
||||
func showList(_ list: ExampleListModel) {
|
||||
let listCoordinator = ListCoordinator(navigationController: navigationController
|
||||
list: list)
|
||||
listCoordinator.start()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In the book I am reading the author created an `ExampleCoordinatorProtocol` with a `func showList(_ list: ExampleListModel)` where the `ExampleCoordinator` implemented it. I think it does not make any sense, however if we might want to inject the coordinator then we might want to relay on an abstraction.
|
||||
|
||||
```swift
|
||||
func scene(_ scene: UIScene,
|
||||
willConnectTo session: UISceneSession,
|
||||
options connectionOptions: UIScene.ConnectionOptions) {
|
||||
|
||||
guard let scene = (scene as? UIWindowScene) else { return }
|
||||
|
||||
let window = UIWindow(windowScene: scene)
|
||||
let navigationController = UINavigationController()
|
||||
exampleCoordinator = ExampleCoordinator(navigationController: navigationController)
|
||||
exampleCoordinator?.start()
|
||||
window.rootViewController = navigationController
|
||||
window.makeKeyAndVisible()
|
||||
self.window = window
|
||||
}
|
||||
```
|
||||
|
||||
## VIPER
|
||||
|
||||
<p>
|
||||
{% svg ../svgs/viper-ownership.svg class="center-image" %}
|
||||
</p>
|
||||
|
||||
|
||||
#### View
|
||||
|
||||
It includes `UIViewController`
|
||||
|
||||
- Made only to preserve elements like Buttons Labels
|
||||
- It sends informations to presenters, and receive messages what to show and knows how
|
||||
|
||||
#### Interactor
|
||||
|
||||
- Receives informations form databases, servers etc.
|
||||
- The book says that the Interactor receive actions from presenter, and returns the result via Delegation Pattern.
|
||||
- The interactor never sends entities to the Presenter
|
||||
|
||||
#### Presenter
|
||||
|
||||
- Is in the centre and serves as a link
|
||||
- Process events from the view and requests data from the Interctor. It receives that as primitives, never Entities.
|
||||
- it handles navigation to the other screens using the Router
|
||||
|
||||
#### Entity
|
||||
|
||||
- Simple models usually data structures
|
||||
- They can only be used by the Interactor
|
||||
|
||||
#### Router
|
||||
|
||||
- Creates screens
|
||||
- Handles navigation, but itself does not know where to go to.
|
||||
- _The book says it is the owner of the `UINavigationController` and UIViewController, but it is contrary to other parts of the book, so I do not know_
|
||||
- Similar to `Coordinator` form MVVM-C
|
||||
|
||||
|
||||
<!--
|
||||
https://github.com/ochococo/Design-Patterns-In-Swift
|
||||
|
||||
https://nalexn.github.io/clean-architecture-swiftui/
|
||||
https://medium.com/@vladislavshkodich/architectures-comparing-for-swiftui-6351f1fb3605
|
||||
|
||||
-->
|
||||
|
||||
**`Entity.swift`**
|
||||
|
||||
```swift
|
||||
struct User: Codable {
|
||||
let name: String
|
||||
}
|
||||
```
|
||||
|
||||
**`Interactor.swift`**
|
||||
|
||||
```swift
|
||||
enum FetchError: Error {
|
||||
case failed
|
||||
}
|
||||
|
||||
protocol AnyInteractor {
|
||||
var presenter: AnyPresenter? { get set }
|
||||
|
||||
func getUsers()
|
||||
}
|
||||
|
||||
class UserInteractor: AnyInteractor {
|
||||
@Injected(\.usersRepository) var usersRepository: AnyUsersRepository
|
||||
|
||||
var presenter: AnyPresenter?
|
||||
|
||||
func getUsers() {
|
||||
usersRepository.getUsers { [weak self] in self?.presenter?.interactorDidFetchUsers(with: $0) }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
**`Presenter.swift`**
|
||||
|
||||
```swift
|
||||
protocol AnyPresenter {
|
||||
var router: AnyRouter? { get set }
|
||||
var interactor: AnyInteractor? { get set }
|
||||
var view: AnyView? { get set }
|
||||
|
||||
func interactorDidFetchUsers(with result: Result<[User], Error>)
|
||||
}
|
||||
|
||||
class UserPresenter: AnyPresenter {
|
||||
func interactorDidFetchUsers(with result: Result<[User], Error>) {
|
||||
switch result {
|
||||
case let .success(users):
|
||||
view?.update(with: users)
|
||||
case let .failure(error):
|
||||
view?.update(with: error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
var router: AnyRouter?
|
||||
|
||||
var interactor: AnyInteractor? {
|
||||
didSet {
|
||||
interactor?.getUsers()
|
||||
}
|
||||
}
|
||||
|
||||
var view: AnyView?
|
||||
}
|
||||
```
|
||||
|
||||
**`Router.swift`**
|
||||
|
||||
```swift
|
||||
typealias EntryPoint = AnyView & UIViewController
|
||||
|
||||
protocol AnyRouter {
|
||||
var entry: EntryPoint? { get }
|
||||
static func start() -> AnyRouter
|
||||
}
|
||||
|
||||
class UserRouter: AnyRouter {
|
||||
var entry: EntryPoint?
|
||||
|
||||
|
||||
static func start() -> AnyRouter {
|
||||
let router = UserRouter()
|
||||
|
||||
var view: AnyView = UserViewController()
|
||||
var presenter: AnyPresenter = UserPresenter()
|
||||
var interactor: AnyInteractor = UserInteractor()
|
||||
|
||||
view.presenter = presenter
|
||||
|
||||
interactor.presenter = presenter
|
||||
|
||||
presenter.router = router
|
||||
presenter.view = view
|
||||
presenter.interactor = interactor
|
||||
|
||||
router.entry = view as? EntryPoint
|
||||
|
||||
return router
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//There are a few retain cycles with view, presenter, router and interactor. One option you can do is to make those protocols conforms to AnyObject, and mark these references as "weak":
|
||||
//1. router's ref to presenter
|
||||
//2. router's ref to view
|
||||
//3. presenter's ref to view
|
||||
//4. interactor's ref to presenter
|
||||
```
|
||||
|
||||
**`View.swift`**
|
||||
|
||||
```swift
|
||||
protocol AnyView {
|
||||
var presenter: AnyPresenter? { get set }
|
||||
|
||||
func update(with users: [User])
|
||||
func update(with error: String)
|
||||
}
|
||||
|
||||
class UserViewController: UIViewController, AnyView {
|
||||
|
||||
|
||||
var presenter: AnyPresenter?
|
||||
|
||||
private let tableView: UITableView = {
|
||||
let tableView = UITableView()
|
||||
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
|
||||
tableView.isHidden = true
|
||||
return tableView
|
||||
}()
|
||||
|
||||
var users = [User]()
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
view.addSubview(tableView)
|
||||
tableView.delegate = self
|
||||
tableView.dataSource = self
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
tableView.frame = view.bounds
|
||||
}
|
||||
|
||||
func update(with users: [User]) {
|
||||
DispatchQueue.main.async {
|
||||
self.users = users
|
||||
self.tableView.reloadData()
|
||||
self.tableView.isHidden = false
|
||||
}
|
||||
}
|
||||
|
||||
func update(with error: String) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension UserViewController: UITableViewDelegate, UITableViewDataSource {
|
||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
users.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
|
||||
cell.textLabel?.text = users[indexPath.row].name
|
||||
return cell
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
<!--
|
||||
RIBs
|
||||
https://github.com/uber/RIBs
|
||||
https://medium.com/swlh/ios-architecture-exploring-ribs-3db765284fd8
|
||||
https://github.com/uber/RIBs/wiki
|
||||
-->
|
||||
|
||||
<!--
|
||||
redux
|
||||
https://medium.com/mackmobile/getting-started-with-redux-in-swift-54e00f323e2b
|
||||
-->
|
||||
|
||||
<!--
|
||||
|
||||
# Factory
|
||||
|
||||
```swift
|
||||
protocol ImageReader {
|
||||
func getDecodeImage() -> DecodedImage
|
||||
}
|
||||
|
||||
class DecodedImage {
|
||||
private var image: String
|
||||
|
||||
init(image: String) {
|
||||
self.image = image
|
||||
}
|
||||
|
||||
var description: String {
|
||||
"\(image): is decoded"
|
||||
}
|
||||
}
|
||||
|
||||
class GifReader: ImageReader {
|
||||
private var decodedImage: DecodedImage
|
||||
|
||||
init(image: String) {
|
||||
self.decodedImage = DecodedImage(image: image)
|
||||
}
|
||||
|
||||
func getDecodeImage() -> DecodedImage {
|
||||
decodedImage
|
||||
}
|
||||
}
|
||||
|
||||
class JpegReader: ImageReader {
|
||||
private var decodedImage: DecodedImage
|
||||
|
||||
init(image: String) {
|
||||
decodedImage = DecodedImage(image: image)
|
||||
}
|
||||
|
||||
func getDecodeImage() -> DecodedImage {
|
||||
decodedImage
|
||||
}
|
||||
}
|
||||
|
||||
func runFactoryExample() {
|
||||
let reader: ImageReader
|
||||
let format = "gif"
|
||||
let image = "example image"
|
||||
|
||||
switch format {
|
||||
case "gif":
|
||||
reader = GifReader(image: image)
|
||||
default:
|
||||
reader = JpegReader(image: image)
|
||||
}
|
||||
|
||||
let decodedImage = reader.getDecodeImage()
|
||||
print(decodedImage.description)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
```swift
|
||||
|
||||
protocol Observer<ValueType> {
|
||||
associatedtype ValueType
|
||||
func update(value: ValueType)
|
||||
}
|
||||
|
||||
struct Subject<T> {
|
||||
private var observers: [(T) -> Void] = []
|
||||
|
||||
mutating func attach<O: Observer>(observer: O) where O.ValueType == T {
|
||||
observers.append { observer.update(value: $0) }
|
||||
}
|
||||
|
||||
func notyfi(value: T) {
|
||||
for observer in observers {
|
||||
observer(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ConcreteObserver: Observer {
|
||||
func update(value: String) {
|
||||
print("received: \(value)")
|
||||
}
|
||||
}
|
||||
|
||||
func runObserverExample() {
|
||||
var subject = Subject<String>()
|
||||
|
||||
let observer1 = ConcreteObserver()
|
||||
subject.attach(observer: observer1)
|
||||
|
||||
let observer2 = ConcreteObserver()
|
||||
subject.attach(observer: observer2)
|
||||
|
||||
subject.notyfi(value: "some string")
|
||||
}
|
||||
|
||||
// Version with more modern syntax
|
||||
/*
|
||||
protocol Observer<ValueType> {
|
||||
associatedtype ValueType
|
||||
func update(value: ValueType)
|
||||
}
|
||||
|
||||
struct Subject<T> {
|
||||
private var observers = Array<any Observer<T>>()
|
||||
|
||||
mutating func attach(observer: any Observer<T>) {
|
||||
observers.append(observer)
|
||||
}
|
||||
|
||||
func notify(value: T) {
|
||||
for observer in observers {
|
||||
observer.update(value: value)
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
```
|
||||
-->
|
30
ios/modularization.md
Normal file
30
ios/modularization.md
Normal file
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
layout: default
|
||||
title: Modularization of an iOS app
|
||||
categories: swift
|
||||
---
|
||||
|
||||
|
||||
### Creating shared product
|
||||
|
||||
1. add submodule to your product `git submodule add git@github.com:artur-gurgul-pro/sharepack.git Sharepack`
|
||||
1. Extract shared code to multi-module package. See `Sharepack`
|
||||
2. Add local package
|
||||
|
||||
TBD
|
||||
|
||||
|
||||
Initialisation of a library
|
||||
|
||||
```bash
|
||||
mkdir MyDeps && cd MyDeps
|
||||
swift package init --type library
|
||||
```
|
||||
|
||||
```bash
|
||||
swift package generate-xcodeproj
|
||||
```
|
||||
|
||||
```bash
|
||||
xcodebuild -resolvePackageDependencies
|
||||
```
|
571
ios/notes.md
Normal file
571
ios/notes.md
Normal file
|
@ -0,0 +1,571 @@
|
|||
---
|
||||
layout: default
|
||||
title: Swift - notes
|
||||
categories: swift
|
||||
---
|
||||
|
||||
#### Autoclosure
|
||||
|
||||
Lazy evaluation of the function's arguments. Instead of eager calculation of values, the clousure is passed, and executed only when needed.
|
||||
|
||||
```swift
|
||||
func test(_ closure: @autoclosure() -> Bool) {
|
||||
<#Code#>
|
||||
}
|
||||
|
||||
test(8==9)
|
||||
```
|
||||
|
||||
#### Destructuring Assignment
|
||||
|
||||
```swift
|
||||
let (name, surname) = ("Artur", "Gurgul")
|
||||
```
|
||||
|
||||
#### Checking if value is in range
|
||||
|
||||
```swift
|
||||
let i = 101
|
||||
if case 100...101 = i {
|
||||
<#Code#>
|
||||
}
|
||||
|
||||
if (100...101).contains(i) {
|
||||
<#Code#>
|
||||
}
|
||||
```
|
||||
|
||||
#### Accessing tuple values
|
||||
|
||||
```swift
|
||||
("value1", "value2").0
|
||||
```
|
||||
|
||||
#### Pattern matching
|
||||
|
||||
```swift
|
||||
enum Example {
|
||||
case first(String)
|
||||
case secund(String)
|
||||
}
|
||||
|
||||
let example: Example = .first("test")
|
||||
```
|
||||
|
||||
```swift
|
||||
switch example {
|
||||
case .first(let value), .secund(let value):
|
||||
print(value)
|
||||
}
|
||||
|
||||
if case let .first(value) = example {
|
||||
print(value)
|
||||
}
|
||||
```
|
||||
|
||||
_print odd numbers_
|
||||
|
||||
```swift
|
||||
for i in 1...100 where i%2 != 0 {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
#### Blurable view in UIKit
|
||||
|
||||
```swift
|
||||
extension Blurable where Self: UIView {
|
||||
func addBlur(_ alpha: CGFloat = 0.5) {
|
||||
let effect = UIBlurEffect(style: .prominent)
|
||||
let effectView = UIVisualEffectView(effect: effect)
|
||||
effectView.frame = self.bounds
|
||||
effectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
effectView.alpha = alpha
|
||||
self.addSubview(effectView)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
extension BackgroundView: Blurable {}
|
||||
```
|
||||
|
||||
## Difference between `@ObservedObject`, `@State`, and `@EnvironmentObject`
|
||||
|
||||
[https://www.hackingwithswift.com/quick-start/swiftui/whats-the-difference-between-observedobject-state-and-environmentobject](https://www.hackingwithswift.com/quick-start/swiftui/whats-the-difference-between-observedobject-state-and-environmentobject)
|
||||
|
||||
>- Use `@State` for simple properties that belong to a single view. They should usually be marked `private`.
|
||||
>- Use `@ObservedObject` for complex properties that might belong to several views. Most times you’re using a reference type you should be using `@ObservedObject` for it.
|
||||
>- Use `@StateObject` once for each observable object you use, in whichever part of your code is responsible for creating it.
|
||||
>- Use `@EnvironmentObject` for properties that were created elsewhere in the app, such as shared data.
|
||||
|
||||
### Cold vs hot observables
|
||||
|
||||
From: Anton Moiseev's Book [“Angular Development with Typescript, Second Edition.”](https://www.manning.com/books/angular-development-with-typescript-second-edition) :
|
||||
|
||||
> **Hot and cold observables**
|
||||
>
|
||||
> There are **two** types of **observables**: hot and cold. The main difference is that a **cold observable** **creates** a **data producer** for **each subscriber**, whereas a **hot observable creates** a **data producer first**, and **each subscriber** gets the **data** from **one producer**, **starting** from **the moment of** **subscription**.
|
||||
>
|
||||
> Let’s compare watching a **movie** on **Netflix** to going into a **movie theater**. Think of yourself as an **observer**. Anyone who decides to watch Mission: Impossible on Netflix will get the entire movie, regardless of when they hit the play button. Netflix creates a new **producer** to stream a movie just for you. This is a **cold observable**.
|
||||
>
|
||||
> If you go to a movie theater and the showtime is 4 p.m., the producer is created at 4 p.m., and the streaming begins. If some people (**subscribers**) are late to the show, they miss the beginning of the movie and can only watch it starting from the moment of arrival. This is a **hot observable**.
|
||||
>
|
||||
> A **cold observable** starts producing data when some code invokes a **subscribe()** function on it. For example, your app may declare an observable providing a URL on the server to get certain products. The request will be made only when you subscribe to it. If another script makes the same request to the server, it’ll get the same set of data.
|
||||
>
|
||||
> A **hot observable** produces data even if no subscribers are interested in the data. For example, an accelerometer in your smartphone produces data about the position of your device, even if no app subscribes to this data. A server can produce the latest stock prices even if no user is interested in this stock.
|
||||
|
||||
|
||||
# Interesting snippets
|
||||
|
||||
<!-- https://swiftbysundell.com/articles/swiftui-views-versus-modifiers/ -->
|
||||
|
||||
# View modifier
|
||||
|
||||
The notification we'll send when a shake gesture happens.
|
||||
|
||||
```swift
|
||||
extension UIDevice {
|
||||
static let deviceDidShakeNotification = Notification
|
||||
.Name(rawValue: "deviceDidShakeNotification")
|
||||
}
|
||||
```
|
||||
|
||||
Override the default behavior of shake gestures to send our notification instead.
|
||||
|
||||
```swift
|
||||
extension UIWindow {
|
||||
open override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
|
||||
if motion == .motionShake {
|
||||
NotificationCenter
|
||||
.default
|
||||
.post(name: UIDevice.deviceDidShakeNotification, object: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A view modifier that detects shaking and calls a function of our choosing.
|
||||
|
||||
```swift
|
||||
struct DeviceShakeViewModifier: ViewModifier {
|
||||
let action: () -> Void
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.onAppear()
|
||||
.onReceive(NotificationCenter
|
||||
.default
|
||||
.publisher(for: UIDevice.deviceDidShakeNotification)) { _ in
|
||||
action()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A View extension to make the modifier easier to use.
|
||||
|
||||
```swift
|
||||
extension View {
|
||||
func onShake(perform action: @escaping () -> Void) -> some View {
|
||||
self.modifier(DeviceShakeViewModifier(action: action))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
An example view that responds to being shaken
|
||||
|
||||
```swift
|
||||
struct ContentView: View {
|
||||
var body: some View {
|
||||
Text("Shake me!")
|
||||
.onShake {
|
||||
print("Device shaken!")
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# Swizzling
|
||||
|
||||
```swift
|
||||
extension UIViewController {
|
||||
@objc dynamic func newViewDidAppear(animated: Bool) {
|
||||
viewDidAppear(animated)
|
||||
print("View appeared")
|
||||
}
|
||||
}
|
||||
|
||||
private let swizzling: Void = {
|
||||
let originalMethod = class_getInstanceMethod(UIViewController.self,
|
||||
#selector(UIViewController.viewDidLoad))
|
||||
let swizzledMethod = class_getInstanceMethod(UIViewController.self,
|
||||
#selector(UIViewController.newViewDidAppear))
|
||||
if let originalMethod = originalMethod, let swizzledMethod = swizzledMethod {
|
||||
method_exchangeImplementations(originalMethod, swizzledMethod)
|
||||
}
|
||||
}()
|
||||
```
|
||||
|
||||
# class vs static values
|
||||
|
||||
```swift
|
||||
class Car {
|
||||
static var start: Int {
|
||||
return 100
|
||||
}
|
||||
|
||||
class var stop: Int {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
class Student: Person {
|
||||
// Not allowed
|
||||
// override static var start: Int {
|
||||
// return 150
|
||||
// }
|
||||
|
||||
override class var stop: Int {
|
||||
return 5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
# Always Publisher example
|
||||
|
||||
```swift
|
||||
public struct Always<Output>: Publisher {
|
||||
public typealias Failure = Never
|
||||
public let output: Output
|
||||
|
||||
public init(_ output: Output) {
|
||||
self.output = output
|
||||
}
|
||||
|
||||
public func receive<S: Subscriber>(subscriber: S)
|
||||
where S.Input == Output, S.Failure == Failure {
|
||||
let subscription = Subscription(output: output,
|
||||
subscriber: subscriber)
|
||||
subscriber.receive(subscription: subscription)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
private extension Always {
|
||||
final class Subscription<S: Subscriber>
|
||||
where S.Input == Output, S.Failure == Failure {
|
||||
private let output: Output
|
||||
private var subscriber: S?
|
||||
|
||||
init(output: Output, subscriber: S) {
|
||||
self.output = output
|
||||
self.subscriber = subscriber
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
extension Always.Subscription: Cancellable {
|
||||
func cancel() {
|
||||
subscriber = nil
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
extension Always.Subscription: Subscription {
|
||||
func request(_ demand: Subscribers.Demand) {
|
||||
var demand = demand
|
||||
while let subscriber = subscriber, demand > 0 {
|
||||
demand -= 1
|
||||
demand += subscriber.receive(output)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# Buffered publisher
|
||||
|
||||
```swift
|
||||
let publisher = PassthroughSubject<Int, Never>()
|
||||
publisher.send(1)
|
||||
let buffered = publisher.buffer(size: 4, prefetch: .keepFull, whenFull: .dropOldest)
|
||||
```
|
||||
|
||||
# Combine publishers
|
||||
|
||||
```swift
|
||||
let publisher1 = [1,2].publisher
|
||||
let publisher2 = [3,4].publisher
|
||||
```
|
||||
|
||||
Emits first and then second
|
||||
|
||||
```swift
|
||||
let combinedPublisher = Publishers
|
||||
.Concatenate(prefix: publisher1.eraseToAnyPublisher(),
|
||||
suffix: publisher2.eraseToAnyPublisher())
|
||||
```
|
||||
|
||||
Combine without ordering
|
||||
|
||||
```swift
|
||||
let combinedPublisher = Publishers.Merge(publisher1.eraseToAnyPublisher(),
|
||||
publisher2.eraseToAnyPublisher())
|
||||
```
|
||||
|
||||
**_Other operators worth to look at_**
|
||||
|
||||
- `Zip` - pair the emitted object, like a zip in the jacket
|
||||
- [`CombineLatest`](https://developer.apple.com/documentation/combine/publisher/combinelatest%28_:%29) - When `publisher1` and `publisher2` emitted some event then the latest values from each are taken and reemitted. From now on each change from either publisher is passed down.
|
||||
|
||||
#### Example of zip
|
||||
|
||||
```swift
|
||||
let numbers = [1, 2, 3, 4].publisher
|
||||
let twos = sequence(first: 2,
|
||||
next: {_ in 2}).publisher
|
||||
numbers
|
||||
.zip(twos)
|
||||
.map { pow(Decimal($0), $1) }
|
||||
.sink(receiveValue: { p in
|
||||
print(p)
|
||||
}).store(in: &cancellables)
|
||||
```
|
||||
|
||||
#### Cancelling a publisher
|
||||
|
||||
```swift
|
||||
let timer = Timer
|
||||
.publish(every: 1.0, on: .main, in: .common)
|
||||
.autoconnect()
|
||||
```
|
||||
|
||||
```swift
|
||||
var counter = 0
|
||||
subscriber = timer
|
||||
.map { _ in counter += 1 }
|
||||
.sink { _ in
|
||||
if counter >= 5 {
|
||||
timer.upstream.connect().cancel()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
It will work similar to this
|
||||
|
||||
```swift
|
||||
subscriber = timer.prefix(5)
|
||||
```
|
||||
|
||||
|
||||
#### Assign
|
||||
|
||||
```swift
|
||||
class Dog {
|
||||
var name: String = ""
|
||||
}
|
||||
|
||||
let dog = Dog()
|
||||
let publisher = Just("Snow")
|
||||
publisher.assign(to:/.name, on: dog)
|
||||
```
|
||||
|
||||
```swift
|
||||
class MyModel: ObservableObject {
|
||||
@Published var lastUpdated: Date = Date()
|
||||
init() {
|
||||
Timer
|
||||
.publish(every: 1.0, on: .main, in: .common)
|
||||
.autoconnect()
|
||||
.assign(to: &$lastUpdated)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
class MyModel: ObservableObject {
|
||||
@Published var id: Int = 0
|
||||
}
|
||||
|
||||
let model = MyModel()
|
||||
Just(100).assign(to: &model.$id)
|
||||
```
|
||||
|
||||
Here's an example of using the `@dynamicMemberLookup` attribute in Swift:
|
||||
|
||||
```swift
|
||||
@dynamicMemberLookup
|
||||
struct DynamicStruct {
|
||||
subscript(dynamicMember member: String) -> String {
|
||||
return "You accessed dynamic member '\(member)'"
|
||||
}
|
||||
}
|
||||
|
||||
let dynamicStruct = DynamicStruct()
|
||||
let result = dynamicStruct.someDynamicMember
|
||||
print(result) // Output: "You accessed dynamic member 'someDynamicMember'"
|
||||
```
|
||||
|
||||
Key Value Coding
|
||||
|
||||
```swift
|
||||
class SomeClass: NSObject {
|
||||
@objc dynamic var name = "Name"
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
object.value(forKey: "name") as String
|
||||
```
|
||||
|
||||
```swift
|
||||
object.setValue("New name", forKey: "name")
|
||||
```
|
||||
|
||||
|
||||
### Create map
|
||||
|
||||
```Swift
|
||||
extension Sequence {
|
||||
func dictionay<T>(keyPath: KeyPath<Element, T>) -> [T: Element] {
|
||||
var dictionary = [T: Element]()
|
||||
|
||||
for elemement in self {
|
||||
let key = elemement[keyPath: keyPath]
|
||||
dictionary[key] = elemement
|
||||
}
|
||||
|
||||
return dictionary
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Compact
|
||||
|
||||
```swift
|
||||
extension Array {
|
||||
public func compact<T>() -> [T] where Element == Optional<T> {
|
||||
compactMap { $0 }
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Swift - currying
|
||||
|
||||
[https://thoughtbot.com/blog/introduction-to-function-currying-in-swift](https://thoughtbot.com/blog/introduction-to-function-currying-in-swift)
|
||||
|
||||
```swift
|
||||
func curry<A, B, C, D>(_ f: @escaping (A, B, C) -> D) -> (A) -> (B) -> (C) -> D {
|
||||
{ a in { b in { c in f(a, b, c) } } }
|
||||
}
|
||||
|
||||
func curry<A, B, C>(_ f: @escaping (A, B) -> C) -> (A) -> (B) -> C {
|
||||
{ a in { b in f(a, b) } }
|
||||
}
|
||||
|
||||
func uncurry<A, B, C>(_ f: @escaping (A) -> (B) -> C) -> (A, B) -> C {
|
||||
{ f($0)($1) }
|
||||
}
|
||||
|
||||
func uncurry<A, B, C, D>(_ f: @escaping (A) -> (B) -> (C) -> D) -> (A, B, C) -> D {
|
||||
{ f($0)($1)($2) }
|
||||
}
|
||||
|
||||
func currying<A, B, C>(_ a: A, _ f: @escaping (A, B) -> C) -> (B) -> C {
|
||||
{ (curry(f))(a)($0) }
|
||||
}
|
||||
|
||||
func currying<A, B, C, D>(_ a: A, _ f: @escaping (A, B, C) -> D) -> (B, C) -> D {
|
||||
{ (curry(f))(a)($0)($1) }
|
||||
}
|
||||
```
|
||||
|
||||
#### Example of usage
|
||||
|
||||
```swift
|
||||
func add(a: Int, b: Int, c: Int) -> Int {
|
||||
a + b + c
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
let adding = curry(add)
|
||||
let adding5 = uncurry(adding(5))
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```swift
|
||||
let adding5 = currying(5, add)
|
||||
```
|
||||
|
||||
then
|
||||
|
||||
```swift
|
||||
print(adding5(6, 7))
|
||||
```
|
||||
|
||||
## Check availability
|
||||
|
||||
[https://www.avanderlee.com/swift/available-deprecated-renamed/](https://www.avanderlee.com/swift/available-deprecated-renamed/)
|
||||
|
||||
```swift
|
||||
if #available(iOS 15, *) {
|
||||
print("This code only runs on iOS 15 and up")
|
||||
} else {
|
||||
print("This code only runs on iOS 14 and lower")
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
guard #available(iOS 15, *) else {
|
||||
print("Returning if iOS 14 or lower")
|
||||
return
|
||||
}
|
||||
|
||||
print("This code only runs on iOS 15 and up")
|
||||
```
|
||||
|
||||
```swift
|
||||
@available(iOS 14, *)
|
||||
final class NewAppIntroduction {
|
||||
// ..
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
@available(iOS, deprecated: 12, obsoleted: 13, message: "We no longer show an app introduction on iOS 14 and up")
|
||||
@available(*, unavailable, renamed: "launchOnboarding")
|
||||
```
|
||||
|
||||
Mapping
|
||||
|
||||
```swift
|
||||
func maping<T>(keyPath: KeyPath<Element, T>) -> [T: Element] {
|
||||
var dictionary = [T: Element]()
|
||||
|
||||
for elemement in self {
|
||||
let key = elemement[keyPath: keyPath]
|
||||
dictionary[key] = elemement
|
||||
}
|
||||
|
||||
return dictionary
|
||||
}
|
||||
```
|
||||
|
||||
### Type aliases
|
||||
|
||||
```swift
|
||||
public typealias Point<T: Numeric> = (x: T, y: T)
|
||||
public typealias MyResult<T> = Result<T, Error>
|
||||
```
|
||||
|
||||
async/await
|
68
ios/recipies.md
Normal file
68
ios/recipies.md
Normal file
|
@ -0,0 +1,68 @@
|
|||
---
|
||||
layout: default
|
||||
title: Recipies
|
||||
categories: swift
|
||||
---
|
||||
|
||||
```swift
|
||||
|
||||
extension UIFont {
|
||||
var bold: UIFont {
|
||||
guard let newDescriptor = fontDescriptor.withSymbolicTraits(.traitBold) else {
|
||||
return self
|
||||
}
|
||||
return UIFont(descriptor: newDescriptor, size: pointSize)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
public struct FontStyle: ViewModifier {
|
||||
let isBold: Bool
|
||||
private let font: Font
|
||||
|
||||
public init(isBold: Bool = false) {
|
||||
self.isBold = isBold
|
||||
self.font = self.font.bold
|
||||
}
|
||||
|
||||
public func body(content: Self.Content) -> some View {
|
||||
content.font(font)
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
public func fontStyle(isBold: Bool = false) -> some View {
|
||||
modifier(FontStyle(isBold: isBold))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Testing
|
||||
|
||||
```swift
|
||||
class ExampleXCTestCase: XCTestCase {
|
||||
let app = XCUIApplication()
|
||||
|
||||
override func tearDown() {
|
||||
app.terminate()
|
||||
}
|
||||
|
||||
override func tearDownWithError() throws {
|
||||
app.terminate()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In tests (it has to be called before `app.launch()`)
|
||||
|
||||
```swift
|
||||
launchEnvironment["key"] = "value"
|
||||
```
|
||||
|
||||
in the app:
|
||||
|
||||
```swift
|
||||
ProcessInfo.processInfo.environment["key"]
|
||||
```
|
418
ios/threading.md
Normal file
418
ios/threading.md
Normal file
|
@ -0,0 +1,418 @@
|
|||
---
|
||||
layout: default
|
||||
title: Threading
|
||||
categories: swift
|
||||
---
|
||||
|
||||
Chunk of jobs that can be performed on the separate thread are arranged into:
|
||||
|
||||
- `Closures` or `Functions`/`Methods` and send to `DispatchQueue`
|
||||
- Classes and send as objects to `OperationQueue`. The queue can perform `Closure` as well
|
||||
|
||||
#### Kinds of jobs
|
||||
|
||||
* Computation intensive jobs: When the thread uses entire processing capability of `CPU`. The reasonable maximum number of the threads is the number of CPU cores.
|
||||
* I/O intensive jobs: In that case we can trigger more threads than we have CPU cores. An optimal of threads is `Threads` = `Cores` / (1-`Blocking Factor`).
|
||||
|
||||
|
||||
# [GCD](https://github.com/apple/swift-corelibs-libdispatch) (Grand Central Dispatch)
|
||||
|
||||
Creating queue. Note without parameter `attributes` we will get serial queue
|
||||
|
||||
```swift
|
||||
let queue = DispatchQueue(label: "important.job",
|
||||
qos: .default,
|
||||
attributes: .concurrent)
|
||||
```
|
||||
|
||||
#### Examples of sending jobs to queues:
|
||||
|
||||
Send to the queue and proceed with the execution in the current context
|
||||
```swift
|
||||
queue.async {
|
||||
<#Code of the job#>
|
||||
}
|
||||
```
|
||||
|
||||
Same as above, but on main thread where UI operations should be done
|
||||
|
||||
```swift
|
||||
DispatchQueue.main.async {
|
||||
<#Code of the job#>
|
||||
}
|
||||
```
|
||||
|
||||
Perform on the global queue
|
||||
|
||||
```swift
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
<#Code of the job#>
|
||||
}
|
||||
```
|
||||
|
||||
Send block that will be executed when all jobs sent at this point are completed. Stop current thread will the queue is done with the sent block.
|
||||
|
||||
```swift
|
||||
queue.sync(flags: [.barrier]) {
|
||||
<#Code of the job#>
|
||||
}
|
||||
```
|
||||
|
||||
Perform job in the time interval
|
||||
|
||||
```swift
|
||||
queue.schedule(after: .init(.now()), interval: .seconds(3)) {
|
||||
<#Code of the job#>
|
||||
}
|
||||
```
|
||||
|
||||
Getting the label of the queue
|
||||
|
||||
```swift
|
||||
extension DispatchQueue {
|
||||
static var label: String {
|
||||
return String(cString: __dispatch_queue_get_label(nil),
|
||||
encoding: .utf8) ?? ""
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `DispatchGroup`
|
||||
|
||||
```swift
|
||||
extension Sequence {
|
||||
public func threadedMap<T>(_ mapper: @escaping (Element) -> T) -> [T] {
|
||||
var output = [T]()
|
||||
let group = DispatchGroup()
|
||||
let queue = DispatchQueue(label: "queue-for--map-result", qos: .background)
|
||||
|
||||
for obj in self {
|
||||
queue.async(group:group) {
|
||||
output.append(mapper(obj))
|
||||
}
|
||||
}
|
||||
|
||||
group.wait()
|
||||
return output
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Mutex
|
||||
|
||||
Performing max 3 tasks at the time
|
||||
|
||||
```swift
|
||||
let semaphore = DispatchSemaphore(value: 3)
|
||||
|
||||
for _ in 0..<15 {
|
||||
queue.async(qos: .background) {
|
||||
semaphore.wait()
|
||||
<#Code of the job#>
|
||||
semaphore.signal()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Locking and unlocking the thread
|
||||
|
||||
**NSLock**
|
||||
|
||||
Example of usage. The concurrent queue becomes sort of serial. One job at the time, but execution in random order.
|
||||
|
||||
```swift
|
||||
let lock = NSLock()
|
||||
for _ in 1...6 {
|
||||
queue.async {
|
||||
lock.lock()
|
||||
<#Code of the job#>
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Mutex**
|
||||
|
||||
Same thing using `pthread` API
|
||||
|
||||
```swift
|
||||
var mutex = pthread_mutex_t()
|
||||
pthread_mutex_init(&mutex, nil)
|
||||
for i in 1...6 {
|
||||
queue.async {
|
||||
pthread_mutex_lock(&mutex)
|
||||
<#Code of the job#>
|
||||
pthread_mutex_unlock(&mutex)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When you are done with the lock, you need to release it
|
||||
|
||||
```swift
|
||||
pthread_mutex_destroy(&mutex)
|
||||
```
|
||||
|
||||
# `NSOperationQueue`
|
||||
|
||||
```swift
|
||||
let queue = OperationQueue()
|
||||
queue.name = "Queue Name"
|
||||
queue.maxConcurrentOperationCount = 1
|
||||
```
|
||||
|
||||
```swift
|
||||
class MyVeryImportantOperation: Operation {
|
||||
override func main() {
|
||||
if isCancelled { return }
|
||||
// Some chunk of time consuming task
|
||||
if isCancelled { return }
|
||||
// Some another chunk of time consuming task
|
||||
// and so on...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Sending jobs to the queue
|
||||
|
||||
```swift
|
||||
let myOperation = MyVeryExpensiveOperation()
|
||||
queue.addOperation(myOperation)
|
||||
```
|
||||
|
||||
```swift
|
||||
queue.addOperation {
|
||||
<#Code of the job#>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Notes `NSOperationQueue` fit better when requires canceling, suspending a block and/or dependency management
|
||||
|
||||
# NSThread
|
||||
|
||||
```swift
|
||||
let thread = Thread {
|
||||
<#code#>
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
let thread = Thread(target: self,
|
||||
selector: #selector(jobMethod:),
|
||||
object: argumentObject)
|
||||
```
|
||||
|
||||
```swift
|
||||
thread.start()
|
||||
```
|
||||
|
||||
Async method
|
||||
|
||||
```swift
|
||||
func doStuff() async {
|
||||
<#code#>
|
||||
}
|
||||
```
|
||||
|
||||
# Span a thread in `NSObject` context
|
||||
|
||||
```swift
|
||||
perform(#selector(job), on: .main, with: nil, waitUntilDone: false)
|
||||
```
|
||||
|
||||
# async/await
|
||||
|
||||
Available from `Swift 5.5`. More info at [docs.swift.org](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/)
|
||||
|
||||
#### Await the task group
|
||||
|
||||
```swift
|
||||
let movies = await withTaskGroup(of: Movie.self) { group in
|
||||
var movies = [Movie]()
|
||||
movies.reserveCapacity(2)
|
||||
for no in 1...2 {
|
||||
group.addTask {
|
||||
return await apiClient.download(movie: no)
|
||||
}
|
||||
}
|
||||
for await movie in group {
|
||||
movies.append(movie)
|
||||
}
|
||||
return movies
|
||||
}
|
||||
```
|
||||
|
||||
**Order of execution**
|
||||
|
||||
```swift
|
||||
print("Before task group")
|
||||
await withTaskGroup(of: Void.self) { group in
|
||||
for item in list {
|
||||
group.addTask {
|
||||
await doSomething()
|
||||
print("Task completed")
|
||||
}
|
||||
}
|
||||
print("For loop completed")
|
||||
}
|
||||
|
||||
print("After task group")
|
||||
```
|
||||
|
||||
```plain
|
||||
1 => print("Before task group")
|
||||
2 => print("For loop completed")
|
||||
3 => print("Task completed")
|
||||
4 => print("Task completed")
|
||||
5 => print("Task completed")
|
||||
6 => print("After task group")
|
||||
```
|
||||
#### `Actor`s
|
||||
|
||||
- Do not support inheritance
|
||||
|
||||
```swift
|
||||
actor TestActor {
|
||||
var property = 1
|
||||
func update(no: Int) {
|
||||
sleep(UInt32.random(in: 1...3))
|
||||
property = no
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
let actor = TestActor()
|
||||
await actor.update(no: 5)
|
||||
```
|
||||
|
||||
#### Call synchronously and parallel
|
||||
|
||||
```swift
|
||||
let firstMovie = await apiClient.download(movie: 1)
|
||||
let secondMovie = await apiClient.download(movie: 2)
|
||||
let movies = [firstMovie, secondMovie]
|
||||
```
|
||||
|
||||
```swift
|
||||
async let firstMovie = apiClient.download(movie: 1)
|
||||
async let secondMovie = apiClient.download(movie: 2)
|
||||
let photos = await [firstMovie, secondMovie]
|
||||
```
|
||||
#### Call the main thread
|
||||
|
||||
```swift
|
||||
let t = Task { @MainActor in
|
||||
print ("update UI")
|
||||
return 5
|
||||
}
|
||||
await print(t.value)
|
||||
```
|
||||
|
||||
#### Call `async` function from a regular method
|
||||
|
||||
```swift
|
||||
func job(no: Int) async -> Int {
|
||||
sleep(UInt32.random(in: 1...3))
|
||||
return no
|
||||
}
|
||||
```
|
||||
|
||||
```swift
|
||||
let result = Task {
|
||||
await job(no: 5)
|
||||
}
|
||||
Task {
|
||||
await print(result.value)
|
||||
}
|
||||
```
|
||||
# pthread
|
||||
|
||||
This is an object that will be sent to the thread
|
||||
|
||||
```swift
|
||||
class ThreadParameter {
|
||||
let paramater = 1
|
||||
}
|
||||
```
|
||||
|
||||
This is a function that will be spanned on the background thread
|
||||
|
||||
```swift
|
||||
func threadedFunction(pointer: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer? {
|
||||
var threadParameter = pointer.load(as: ThreadParameter.self)
|
||||
<#Code of the job#>
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
Creating a `pthread`
|
||||
|
||||
```swift
|
||||
var myThread: pthread_t? = nil
|
||||
let threadParameter = ThreadParameter()
|
||||
var pThreadParameter = UnsafeMutablePointer<ThreadParameter>.allocate(capacity:1)
|
||||
pThreadParameter.pointee = threadParameter
|
||||
|
||||
let result = pthread_create(&myThread, nil, threadedFunction, pThreadParameter)
|
||||
```
|
||||
|
||||
waiting for finishing the thread
|
||||
|
||||
```swift
|
||||
if result == 0, let myThread {
|
||||
pthread_join(myThread,nil)
|
||||
}
|
||||
```
|
||||
|
||||
# Span threads using `Combine` framework
|
||||
|
||||
**`subscribe(on:)`**
|
||||
|
||||
Quotes from: [https://trycombine.com/posts/subscribe-on-receive-on/](https://trycombine.com/posts/subscribe-on-receive-on/)
|
||||
|
||||
> `subscribe(on:)` sets the scheduler on which you’d like the current subscription to be “managed” on. This operator sets the scheduler to use for creating the subscription, cancelling, and requesting input.
|
||||
>
|
||||
> ... `subscribe(on:)` sets the scheduler to subscribe the upstream on.
|
||||
>
|
||||
> A side effect of subscribing on the given scheduler is that `subscribe(on:)` also changes the scheduler for its downstream ....
|
||||
|
||||
Subscription is done once and calling second time the `subscribe(on: )` have no effect.
|
||||
|
||||
`subscribe(on: DispatchQueue.global(qos: .background))` queues on this call → `func request(_ demand: Subscribers.Demand)`
|
||||
|
||||
**`receive(on:)`**
|
||||
|
||||
> The correct operator to use to change the scheduler where downstream output is delivered is `receive(on:)`.
|
||||
>
|
||||
> On other words `receive(on:)` sets the scheduler where the downstream receives output on.
|
||||
>
|
||||
> You can use `receive(on:)` similarly to `subscribe(on:)`. You can use multiple `receive(on:)` operators and that will always change the downstream scheduler
|
||||
|
||||
# Usage of `RunLoop`
|
||||
|
||||
<!-- Diffrence between queue and runloop https://www.avanderlee.com/combine/runloop-main-vs-dispatchqueue-main/ -->
|
||||
|
||||
Doncumentation on [RunLoop](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html)
|
||||
|
||||
> The purpose of a run loop is to keep your thread busy when there is work to do and put your thread to sleep when there is non. - Apple
|
||||
|
||||
# Measure a performance time
|
||||
|
||||
```swift
|
||||
let startTime = CFAbsoluteTimeGetCurrent()
|
||||
<#Code of the job#>
|
||||
let endTime = CFAbsoluteTimeGetCurrent()
|
||||
print("Duration:\(endTime - startTime) seconds")
|
||||
```
|
||||
|
||||
#### Stop thread for some time
|
||||
|
||||
```swift
|
||||
sleep(2)
|
||||
```
|
||||
|
||||
```swift
|
||||
try await Task.sleep(until: .now + .seconds(2), clock: .continuous)
|
||||
```
|
350
js/notes.md
Normal file
350
js/notes.md
Normal file
|
@ -0,0 +1,350 @@
|
|||
---
|
||||
layout: default
|
||||
title: Javascript - notes
|
||||
categories: programming
|
||||
path: js/notes
|
||||
---
|
||||
|
||||
Useful tool: Why did you render
|
||||
https://react.dev/learn/react-developer-tools
|
||||
|
||||
### Currying - JavaScript
|
||||
|
||||
|
||||
```js
|
||||
function currying(fn, ...args) {
|
||||
return (..._arg) => {
|
||||
return fn(...args, ..._arg);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
function sum(a,b,c) {
|
||||
return a + b + c
|
||||
}
|
||||
let add = currying(sum,10);
|
||||
```
|
||||
|
||||
#### Advanced implementation
|
||||
|
||||
```js
|
||||
function curry(func) {
|
||||
return function curried(...args) {
|
||||
if (args.length >= func.length) {
|
||||
return func.apply(this, args)
|
||||
} else {
|
||||
return function(...args2) {
|
||||
return curried.apply(this, args.concat(args2))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Usage
|
||||
|
||||
```js
|
||||
function sum(a, b, c) {
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
let curriedSum = curry(sum);
|
||||
|
||||
// Callable normally
|
||||
curriedSum(1, 2, 3)
|
||||
|
||||
// Currying of 1st argument
|
||||
curriedSum(1)(2,3)
|
||||
|
||||
// Currying all arguments
|
||||
curriedSum(1)(2)(3)
|
||||
```
|
||||
|
||||
|
||||
### Closures
|
||||
|
||||
```javascript
|
||||
var NextClosure = function() {
|
||||
var a = 0;
|
||||
return function next() {
|
||||
a++
|
||||
return a
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```javascript
|
||||
var next = NextClosure()
|
||||
|
||||
console.log(next())
|
||||
console.log(next())
|
||||
console.log(next())
|
||||
```
|
||||
|
||||
|
||||
### Manipulating SVG
|
||||
|
||||
```html
|
||||
<svg id="display" width="50" height="50" style="background-color: aqua;"></svg>
|
||||
```
|
||||
|
||||
|
||||
```javascript
|
||||
let element = document.createElementNS("http://www.w3.org/2000/svg", 'rect')
|
||||
element.setAttribute("x", 25)
|
||||
element.setAttribute("y", 25)
|
||||
element.setAttribute("width", 25)
|
||||
element.setAttribute("height",25)
|
||||
element.setAttribute("fill", "black")
|
||||
let display = document.getElementById("display")
|
||||
display.appendChild(element)
|
||||
```
|
||||
|
||||
### Operations on arrays
|
||||
|
||||
```js
|
||||
let test = [1,2,3,4,5,6,7]
|
||||
console.log(test.splice(-5, 5)) // [ 3, 4, 5, 6, 7 ]
|
||||
console.log(test) // [ 1, 2 ]
|
||||
```
|
||||
|
||||
- `test.splice(-5, 5)` counting from the end
|
||||
- `test.splice(5)` from 5th to the end of the array
|
||||
|
||||
```js
|
||||
// Take just first element
|
||||
console.log(process.argv.shift()
|
||||
```
|
||||
|
||||
|
||||
## Call function in place
|
||||
|
||||
|
||||
```js
|
||||
(function waitForAsync() {
|
||||
if (end) {
|
||||
console.log("Synchronous operation can proceed now")
|
||||
} else {
|
||||
setImmediate(waitForAsync)
|
||||
}
|
||||
})()
|
||||
```
|
||||
|
||||
## Spawn process
|
||||
|
||||
```js
|
||||
const { spawn } = require('child_process')
|
||||
const child = spawn(command, params, {cwd: process.cwd()});
|
||||
|
||||
//for text chunks
|
||||
child.stdout.setEncoding('utf8')
|
||||
|
||||
child.stdout.on('data', (chunk) => {
|
||||
//process.stdout.write(chunk)
|
||||
})
|
||||
|
||||
// since these are streams, you can pipe them elsewhere
|
||||
// child.stderr.pipe(dest);
|
||||
|
||||
child.on('close', (code) => {
|
||||
console.log(`ENDED: ${command} ${params.join(" ")} $ => ${code}`);
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
## Handling working directory
|
||||
|
||||
```js
|
||||
function dir(wd, commands) {
|
||||
let saveWD = process.cwd()
|
||||
process.chdir(wd)
|
||||
commands()
|
||||
process.chdir(saveWD)
|
||||
}
|
||||
```
|
||||
|
||||
## Execute script loaded from string
|
||||
|
||||
|
||||
```js
|
||||
const util = require('util')
|
||||
const vm = require('vm')
|
||||
|
||||
const sandbox = {
|
||||
animal: 'cat',
|
||||
count: 2
|
||||
}
|
||||
|
||||
const script = new vm.Script('count += 1; name = "kitty"')
|
||||
const context = new vm.createContext(sandbox)
|
||||
|
||||
for (let i = 0; i < 10; ++i) {
|
||||
script.runInContext(context)
|
||||
}
|
||||
|
||||
console.log(util.inspect(sandbox))
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```js
|
||||
primes = [ 2, 3, 5, 7, 11 ]
|
||||
subset = eval('primes.filter(each => each > 5)')
|
||||
console.log(subset)
|
||||
```
|
||||
|
||||
```js
|
||||
(async () => {
|
||||
let file = await readSource()
|
||||
})()
|
||||
```
|
||||
|
||||
|
||||
mjs => node module, allows to use await/async at the top level
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "om",
|
||||
"version": "1.0.0",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "ts-node src/index.ts",
|
||||
"build": "tsc && cp src/om dist/om",
|
||||
"run": "node dist/index.js",
|
||||
"watch": "nodemon --watch 'src/**' --ext 'ts,json' --ignore 'src/**/*.spec.ts' --exec 'tsc || true'"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"commander": "^12.0.0",
|
||||
"pg": "^8.11.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "^3.1.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Make executable
|
||||
|
||||
```js
|
||||
#!/usr/bin/env node
|
||||
require(".")
|
||||
```
|
||||
|
||||
### Get all function argments
|
||||
|
||||
```js
|
||||
function func(1,2 3) {
|
||||
console.log(arguments)
|
||||
}
|
||||
```
|
||||
|
||||
## String literals and parentheses-less calls
|
||||
|
||||
|
||||
```javascript
|
||||
const showstring = (str, ...values) => {
|
||||
console.log(str);
|
||||
console.log(values)
|
||||
}
|
||||
const name = 'John'
|
||||
showstring`Hello World ${name} ${name} ${1 + 2} and more`;
|
||||
```
|
||||
|
||||
```javascript
|
||||
// str
|
||||
[
|
||||
"Hello World ",
|
||||
" ",
|
||||
" ",
|
||||
" and more"
|
||||
]
|
||||
|
||||
// values
|
||||
[
|
||||
"John",
|
||||
"John",
|
||||
3
|
||||
]
|
||||
```
|
||||
|
||||
## Generators
|
||||
|
||||
```javascript
|
||||
function* zip(...arrays){
|
||||
const maxLength = arrays.reduce((max, curIterable) => curIterable.length > max ? curIterable.length: max, 0);
|
||||
for (let i = 0; i < maxLength; i++) {
|
||||
yield arrays.map(array => array[i]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## `this` and inheritance
|
||||
|
||||
```js
|
||||
function sum(b) {
|
||||
return this.a + b;
|
||||
}
|
||||
|
||||
sum.call({a: 4}, 5) // 9
|
||||
const newSum = sum.bind({a: 4})
|
||||
newSum(3) // 7
|
||||
```
|
||||
|
||||
|
||||
```js
|
||||
|
||||
function User() {
|
||||
this.id = 5
|
||||
|
||||
this.test1 = () => {
|
||||
console.log(this.id)
|
||||
}
|
||||
|
||||
this.test2 = function() {
|
||||
console.log(this.id)
|
||||
}
|
||||
}
|
||||
|
||||
User.prototype = {
|
||||
|
||||
show: function () {
|
||||
console.log(`user id = ${this.id}`)
|
||||
}
|
||||
}
|
||||
|
||||
let user = new User()
|
||||
|
||||
user.show() // user id = 5
|
||||
user.test1() // 5
|
||||
user.test2() // 5
|
||||
|
||||
user.__proto__ = {
|
||||
show: function() {
|
||||
console.log(`user: ${this.name}`)
|
||||
}
|
||||
}
|
||||
|
||||
user.name = "Artur"
|
||||
user.show() // user: Artur
|
||||
|
||||
user.__proto__ = {
|
||||
show: () => {
|
||||
console.log(`user: ${this.name}`)
|
||||
}
|
||||
}
|
||||
|
||||
user.show() // user:
|
||||
|
||||
```
|
||||
|
567
linux/installation.md
Normal file
567
linux/installation.md
Normal file
|
@ -0,0 +1,567 @@
|
|||
---
|
||||
layout: default
|
||||
title: Linux installation
|
||||
categories: linux
|
||||
---
|
||||
In case of installation on VM make the disk image
|
||||
|
||||
```
|
||||
qemu-img create -f qcow2 debian.qcow2 16G
|
||||
```
|
||||
|
||||
Other format option
|
||||
|
||||
```
|
||||
qemu-img create -f raw debian.raw 16G
|
||||
-drive file=disk.raw,format=raw
|
||||
```
|
||||
|
||||
|
||||
Start the VM with [bios](https://github.com/clearlinux/common/raw/master/OVMF.fd)
|
||||
|
||||
```bash
|
||||
qemu-system-x86_64 -bios OVMF.fd -m 1G -drive file=debian.qcow2,format=qcow2 \
|
||||
-cdrom debian-12.2.0-amd64-netinst.iso
|
||||
```
|
||||
|
||||
Go through the installation process and then power off the VM
|
||||
|
||||
Start VM with command
|
||||
|
||||
```bash
|
||||
qemu-system-x86_64 -bios OVMF.fd -m 1G -smp 6 \
|
||||
-net user,hostfwd=tcp::2222-:22 -net nic \
|
||||
-drive file=debian.qcow2,format=qcow2
|
||||
```
|
||||
|
||||
## Prepare install media on macOS
|
||||
|
||||
Insert and unmount an USB stick
|
||||
|
||||
```bash
|
||||
diskutil unmount /dev/disk2s1
|
||||
```
|
||||
|
||||
Here is how we can list all the disks attached to the system
|
||||
|
||||
```bash
|
||||
diskutil list
|
||||
```
|
||||
|
||||
Writing image to the USB device (notice that we do not write to a partition)
|
||||
|
||||
```bash
|
||||
sudo dd if=archlinux-2021.01.01-x86_64.iso of=/dev/disk2
|
||||
```
|
||||
|
||||
then flush the data by ejecting the drive
|
||||
|
||||
```bash
|
||||
sudo sync
|
||||
diskutil eject /dev/disk2
|
||||
```
|
||||
|
||||
|
||||
# Debian sid
|
||||
|
||||
This is the Debian setup where most of the examples shown on the website should work.
|
||||
|
||||
Download links:
|
||||
|
||||
<!-- https://www.debian.org/CD/live/ -->
|
||||
|
||||
- [amd64 - Install disc](https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-12.2.0-amd64-netinst.iso)
|
||||
- [amd64 - Live disc](https://cdimage.debian.org/debian-cd/current-live/amd64/iso-hybrid/)
|
||||
|
||||
During installation select SSH server, standard system utilities and no desktop
|
||||
|
||||
#### Allowing login root user from the network
|
||||
|
||||
This is our testing installation, so we do not care about security, but easiness and convenience.
|
||||
|
||||
```bash
|
||||
nano /etc/ssh/sshd_config
|
||||
```
|
||||
|
||||
`Ctrl-o` `Ctrl-x`
|
||||
|
||||
Put this line in the file
|
||||
```plain
|
||||
PermitRootLogin yes
|
||||
```
|
||||
|
||||
and then
|
||||
|
||||
```bash
|
||||
systemctl restart sshd
|
||||
```
|
||||
|
||||
From now on you can log in to the VM using ssh connection
|
||||
|
||||
```
|
||||
ssh -p 2222 user@localhost
|
||||
```
|
||||
|
||||
### Making it the sid
|
||||
|
||||
|
||||
/etc/apt/sources.list
|
||||
|
||||
```
|
||||
deb https://deb.debian.org/debian/ sid main contrib non-free non-free-firmware
|
||||
deb-src https://deb.debian.org/debian/ sid main contrib non-free non-free-firmware
|
||||
```
|
||||
|
||||
```bash
|
||||
apt update
|
||||
apt upgrade
|
||||
apt dist-upgrade
|
||||
apt autoremove
|
||||
|
||||
apt install firmware-linux-nonfree
|
||||
```
|
||||
|
||||
### Tools
|
||||
|
||||
|
||||
```sh
|
||||
apt install neovim clang
|
||||
```
|
||||
|
||||
**Swift**
|
||||
|
||||
Dependencies
|
||||
|
||||
```sh
|
||||
apt install build-essential libcurl4-openssl-dev binutils git gnupg2 libc6-dev \
|
||||
libedit2 libsqlite3-0 libxml2-dev libz3-dev pkg-config tzdata \
|
||||
tzdata unzip zlib1g-dev libgcc-9-dev libncurses-dev \
|
||||
libstdc++-9-dev
|
||||
```
|
||||
|
||||
Missing dependencies for Debian sid
|
||||
|
||||
```text
|
||||
libpython3.8
|
||||
```
|
||||
|
||||
Installed instead
|
||||
|
||||
```plain
|
||||
apt install libpython3.10-dev python3-clang python3-lldb
|
||||
```
|
||||
|
||||
|
||||
Downloading and installing
|
||||
|
||||
```
|
||||
wget https://download.swift.org/swift-5.9.1-release/ubuntu2204/swift-5.9.1-RELEASE/swift-5.9.1-RELEASE-ubuntu22.04.tar.gz
|
||||
|
||||
tar -xf swift-5.9.1-RELEASE-ubuntu22.04.tar.gz
|
||||
mv swift-5.9.1-RELEASE-ubuntu22.04 /opt/swift-5.9.1
|
||||
```
|
||||
|
||||
|
||||
add this line to `/etc/profile` so the path will be added for all the users
|
||||
|
||||
```plain
|
||||
export PATH="$PATH:/opt/swift-5.9.1/usr/bin"
|
||||
```
|
||||
|
||||
# Archlinux
|
||||
|
||||
Links:
|
||||
- [torrents with ISO files](https://archlinux.org/releng/releases/)
|
||||
- [latest ISO version from HTTP mirror](https://geo.mirror.pkgbuild.com/iso/latest/)
|
||||
- [Offline: Most recent installation guide](https://wiki.archlinux.org/index.php/Offline_installation)
|
||||
- [UEFI: Most recent installation guide](https://wiki.archlinux.org/title/installation_guideI)
|
||||
|
||||
# Installation
|
||||
|
||||
Note: in case case it is Mac mini. Hold alt (option) button on boot up and select the install disk.
|
||||
|
||||
Here are commands to check what discs are attached to the system
|
||||
|
||||
```bash
|
||||
cat /proc/partitions
|
||||
ls /dev/[s|x|v]d*
|
||||
lsblk
|
||||
fdisk –l
|
||||
ls /dev | grep ‘^[s|v|x][v|d]’$*
|
||||
```
|
||||
|
||||
<!--
|
||||
The install disk has free space which we can use (for example to create install scripts in case we use the install disk several times)
|
||||
|
||||
cfdisk /dev/sdb
|
||||
|
||||
Select thf freespace and hit `[New]` and `Enter` => `[Write]` => `enter` => `yes` =>`Enter` => `[Quit]`
|
||||
|
||||
Now we have unformatted partition. To screate FAT32 execute this commend
|
||||
|
||||
|
||||
|
||||
Mount it as home folder
|
||||
mount /dev/sdb4 /root
|
||||
# and go to the new home root
|
||||
cd
|
||||
|
||||
# this command was given by arch wiki but do not work for me
|
||||
# https://wiki.archlinux.org/index.php/FAT
|
||||
|
||||
|
||||
|
||||
Now I realized I can edit system, so the partition that I have just created I can mount on start. There are steps I took.
|
||||
|
||||
- On the USB stick there is prtition named Gap1. I remove it becouse it seems to not be needed. I use `gparted` for that.
|
||||
- Shrink vfat partition to 8000 MiB and place it at the and.
|
||||
|
||||
- Crate partitions to look like this: `[1: 628.97MiB]` `[2: 2.2 GiB]` `[3: 59 MiB]` `[4: 4000 MiB]` `[5: 8000MiB]`
|
||||
1. `ARCH202101`: It is the oryginal partition ISO9660 I had after writing image
|
||||
2. Freespace that will might be used when I edit the first partition
|
||||
3. The UEFI partition where is placed bootloader that starts sysyem that is located on the first partition. This partition comes form the orygunal image.
|
||||
4. `CHROOT`: This parition will contains files of the installer system. They are placed on Ext4 partition, so we can edit files and regenerate ISO file from it.
|
||||
5. Home folder for root user, so when we can write scripts, store files so we can use them in other instalation process.
|
||||
|
||||
Now we copy read only files from read only system to writable partition
|
||||
|
||||
sudo mount -o loop /media/artur/ARCH202101/arch/x86_64/airfs.sfs /mnt
|
||||
sudo cp -T /mnt /media/artur/CHROOT
|
||||
-->
|
||||
|
||||
Update packages manager
|
||||
|
||||
```bash
|
||||
pacman -Sy
|
||||
```
|
||||
|
||||
|
||||
Make a partition table
|
||||
|
||||
```bash
|
||||
parted -s /dev/sda mktable GPT
|
||||
```
|
||||
|
||||
### Create partitions
|
||||
|
||||
In this case:
|
||||
|
||||
- 300MB → UEFI
|
||||
- 16GB → Swap
|
||||
- Rest → System
|
||||
|
||||
List all types of partitions
|
||||
|
||||
```bash
|
||||
sfdisk -T
|
||||
```
|
||||
|
||||
#### First way: Using `sfdisk`
|
||||
|
||||
```bash
|
||||
sfdisk /dev/sda << EOF
|
||||
,300,ef
|
||||
,16000,S,h
|
||||
;
|
||||
EOF
|
||||
```
|
||||
|
||||
or pipe to the program like
|
||||
|
||||
```bash
|
||||
echo ',,c;' | sfdisk /dev/sdd
|
||||
```
|
||||
|
||||
#### Secund way: Using `fdisk`
|
||||
|
||||
```bash
|
||||
fdisk /dev/sda << FDISK_CMDS
|
||||
g
|
||||
n
|
||||
1
|
||||
|
||||
+300MiB
|
||||
t
|
||||
1
|
||||
n
|
||||
2
|
||||
|
||||
+16GiB
|
||||
t
|
||||
2
|
||||
19
|
||||
n
|
||||
3
|
||||
|
||||
|
||||
t
|
||||
3
|
||||
20
|
||||
w
|
||||
FDISK_CMDS
|
||||
```
|
||||
|
||||
#### Therd way
|
||||
|
||||
```bash
|
||||
cfdisk /dev/sda
|
||||
```
|
||||
|
||||
Formatting
|
||||
|
||||
```bash
|
||||
mkfs.fat -F32 /dev/sda1
|
||||
mkswap /dev/sda2
|
||||
mkfs.ext4 /dev/sda3
|
||||
```
|
||||
|
||||
Mounting partitions
|
||||
|
||||
```bash
|
||||
mount /dev/sda3 /mnt
|
||||
swapon /dev/sda2
|
||||
```
|
||||
|
||||
Installing base packages
|
||||
|
||||
```bash
|
||||
pacstrap /mnt base base-devel linux linux-firmware
|
||||
```
|
||||
|
||||
Chrooting
|
||||
|
||||
```bash
|
||||
arch-chroot /mnt << CHROOT
|
||||
#commands
|
||||
CHROOT
|
||||
```
|
||||
|
||||
or just `arch-chroot /mnt` and then commands
|
||||
|
||||
```bash
|
||||
echo "archlinux" > /etc/hostname
|
||||
sed -i "s/#en_US/en_US/g" /etc/locale.gen
|
||||
locale-gen
|
||||
echo LANG=en_US.UTF-8 > /etc/locale.conf
|
||||
export LANG=en_US.UTF-8
|
||||
ln -s /usr/share/zoneinfo/Europe/Warsaw /etc/localtime
|
||||
hwclock --systohc --utc
|
||||
```
|
||||
|
||||
#### Run 32bit apps
|
||||
|
||||
open → `/etc/pacman.conf` and uncomment `[multilib]` section
|
||||
|
||||
Installing additional software
|
||||
|
||||
```bash
|
||||
pacman -Syu
|
||||
pacman -S zsh --noconfirm
|
||||
```
|
||||
|
||||
#### Setup users
|
||||
|
||||
Set password for root
|
||||
|
||||
```bash
|
||||
passwd
|
||||
```
|
||||
|
||||
Create user
|
||||
|
||||
```bash
|
||||
useradd -mg users -G wheel,storage,power -s /usr/bin/zsh user
|
||||
```
|
||||
|
||||
```bash
|
||||
passwd user
|
||||
```
|
||||
|
||||
You can force user to change password
|
||||
|
||||
```bash
|
||||
chage -d 0 user
|
||||
```
|
||||
|
||||
sudoers
|
||||
|
||||
```bash
|
||||
visudo
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
cat >> /etc/sudoers <<EOL
|
||||
%wheel ALL=(ALL) NOPASSWD: ALL
|
||||
EOL
|
||||
```
|
||||
|
||||
### Setting up bootloader
|
||||
|
||||
```bash
|
||||
pacman -S grub efibootmgr dosfstools os-prober mtools --noconfirm
|
||||
mkdir /boot/EFI
|
||||
mount /dev/sda1 /boot/EFI
|
||||
grub-install --target=x86_64-efi --efi-directory=/boot/EFI --bootloader-id=grub_uefi --recheck
|
||||
|
||||
grub-mkconfig -o /boot/grub/grub.cfg
|
||||
```
|
||||
|
||||
|
||||
then `exit` and
|
||||
|
||||
Generate `fstab`
|
||||
|
||||
```bash
|
||||
genfstab -U -p /mnt >> /mnt/etc/fstab
|
||||
```
|
||||
|
||||
|
||||
- `umount -a`
|
||||
- `poweroff`
|
||||
|
||||
|
||||
## Install any Linux out from VM
|
||||
|
||||
Create OS archive locally using rsync
|
||||
|
||||
```bash
|
||||
rsync -aAXHSv /* /root/winux --exclude={/dev/*,/proc/*,/sys/*,/tmp/*,/run/*,/mnt/*,/root/*,/media/*,/lost+found,/home/*/.gvfs}
|
||||
tar -cvjSf winux.tar.bz2 winux
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
tar -cvjSf winux.tar.bz2 / --exclude={/dev/*,/proc/*,/sys/*,/tmp/*,/run/*,/mnt/*,/root/*,/media/*,/lost+found,/home/*/.gvfs}
|
||||
```
|
||||
|
||||
Create archive and move it to the host
|
||||
|
||||
```
|
||||
ssh -p 2222 root@localhost 'tar -cvjS / --exclude={/dev/*,/proc/*,/sys/*,/tmp/*,/run/*,/mnt/*,/root/*,/media/*,/lost+found,/home/*/.gvfs} --to-stdout' > winux.tar.bz2
|
||||
```
|
||||
|
||||
#### Extract files to the destination drive
|
||||
|
||||
```bash
|
||||
tar -xvjf winux.tar.bz2 -C /mnt/sys
|
||||
```
|
||||
|
||||
https://wiki.archlinux.org/title/Moving_an_existing_install_into_(or_out_of)_a_virtual_machine
|
||||
|
||||
Then generate fstab
|
||||
|
||||
```bash
|
||||
genfstab -U /mnt/sys > /mnt/sys/etc/fstab
|
||||
```
|
||||
|
||||
<!--
|
||||
## More customisation
|
||||
|
||||
#### DHCP
|
||||
|
||||
```
|
||||
pacman -S dhcpcd --noconfirm
|
||||
```
|
||||
|
||||
```s
|
||||
systemctl enable dhcpcd
|
||||
systemctl start dhcpcd
|
||||
```
|
||||
|
||||
```bash
|
||||
dhcpcd enp3s0f0
|
||||
```
|
||||
|
||||
```
|
||||
ip a
|
||||
|
||||
ping -c2 google.com
|
||||
```
|
||||
|
||||
## Sway
|
||||
|
||||
```bash
|
||||
pacman -S git meson wlroots wayland wayland-protocols \
|
||||
pcre2 json-c pango cairo gdk-pixbuf2
|
||||
```
|
||||
|
||||
```bash
|
||||
git clone https://github.com/swaywm/sway.git
|
||||
```
|
||||
|
||||
building
|
||||
|
||||
```bash
|
||||
git checkout v1.8
|
||||
```
|
||||
|
||||
Disable waring as errors in `meson.build` 10 line `'werror=false'`
|
||||
|
||||
```bash
|
||||
meson build/
|
||||
ninja -C build/
|
||||
sudo ninja -C build/ install
|
||||
```
|
||||
|
||||
# Interesting software
|
||||
|
||||
- zathura - PDF reader
|
||||
- poppler => gives pdftotext
|
||||
|
||||
|
||||
|
||||
# Building own environment (inside QEMU) - Archlinux
|
||||
|
||||
```bash
|
||||
pacman -S qemu-guest-agent
|
||||
systemctl enable --now qemu-guest-agent
|
||||
```
|
||||
|
||||
### install software to build and run
|
||||
|
||||
```bash
|
||||
pacman -S egl-wayland meson wlroots wayland wayland-protocols pcre2 json-c pango cairo gdk-pixbuf2 scdoc cmake less xorg-xwayland xdg-desktop-portal-wlr xdg-desktop-portal-gtk ttf-bitstream-vera gnu-free-fonts noto-fonts ttf-croscore ttf-dejavu ttf-droid ttf-ibm-plex ttf-liberation xorg
|
||||
```
|
||||
|
||||
```bash
|
||||
git clone https://github.com/swaywm/sway.git
|
||||
cd sway/
|
||||
git checkout v1.8
|
||||
|
||||
meson build/
|
||||
ninja -C build/
|
||||
sudo ninja -C build/ install
|
||||
```
|
||||
|
||||
|
||||
# Building own environment (inside QEMU) - Debian
|
||||
|
||||
|
||||
|
||||
```
|
||||
apt install -y libnvidia-egl-wayland-dev meson libwlroots-dev wayland-utils wayland-protocols libpcre2-dev libjson-c-dev \
|
||||
libpango-1.0-0 libpangocairo-1.0-0 libcairo2-dev libpango1.0-dev libgdk-pixbuf2.0-dev scdoc cmake xwayland bochs bochs-sdl bochs-term bochs-wx bochs-x \
|
||||
vgabios xscreensaver-gl xscreensaver-gl-extra mesa-vulkan-drivers mesa-utils-bin mesa-utils mesa-drm-shim mesa-common-dev \
|
||||
libglx-mesa0 libglw1-mesa-dev libgl1-mesa-dri libglapi-mesa libgl1-mesa-dri libgbm-dev weston libweston-12-dev curl htop \
|
||||
openssh-server neovim zsh ruby python3 cmake rust-all ruby-dev ruby-full build-essential npm r-base r-base-dev fzf rclone \
|
||||
rtorrent htop bundler neomutt golang ghc cabal-install gulp npm neovim mc tree cmake scala maven imagemagick hexedit erlang \
|
||||
nasm binutils nim tmux wget httpie yarn meson util-linux ninja-build git fakeroot build-essential ncurses-dev xz-utils \
|
||||
libssl-dev bc flex libelf-dev bison python3-pip libisl-dev texinfo libmpfr-dev libmpc-dev libgmp3-dev genisoimage clang \
|
||||
libboost-tools-dev libboost-dev libboost-system-dev gcc g++ make pkg-config libgtk-3-dev libgstreamer1.0-dev \
|
||||
libgstreamer-plugins-base1.0-dev cmake ninja-build coreutils libxml2-dev libsqlite3-dev libicu-dev libxslt-dev libjpeg-dev \
|
||||
libpng-dev libwebp-dev libsecret-1-dev binutils git gnupg2 libc6-dev libcurl4 libedit2 libgcc-9-dev libsqlite3-0 libstdc++-9-dev \
|
||||
libxml2 libz3-dev pkg-config tzdata zlib1g-dev python3 apt-file libwayland-server++1 libwayland-server0 freerdp2-dev \
|
||||
freerdp2-wayland libpam-freerdp2-dev libavutil-dev libavcodec-dev libavutil-dev libavformat-dev fossil krusader texlive-full \
|
||||
openssh-server neovim zsh ruby python3 cmake binutils git gnupg2 libc6-dev libcurl4 libedit2 libgcc-9-dev libsqlite3-0 \
|
||||
libstdc++-9-dev libxml2 libz3-dev pkg-config tzdata zlib1g-dev python3 rust-all ruby-dev ruby-full build-essential npm \
|
||||
r-base r-base-dev fzf rclone rtorrent htop bundler neomutt golang ghc cabal-install gulp npm neovim mc tree cmake scala \
|
||||
maven imagemagick hexedit erlang nasm binutils nim tmux wget httpie yarn meson util-linux ninja-build python3-pip libisl-dev \
|
||||
texinfo libmpfr-dev libmpc-dev libgmp3-dev genisoimage clang libboost-tools-dev libboost-dev libboost-system-dev git fakeroot \
|
||||
build-essential ncurses-dev xz-utils libssl-dev bc flex libelf-dev bison gcc g++ make pkg-config cmake ninja-build \
|
||||
coreutils libgtk-3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libxml2-dev libsqlite3-dev libicu-dev libxslt-dev \
|
||||
libjpeg-dev libpng-dev libwebp-dev libsecret-1-dev
|
||||
```
|
||||
-->
|
31
linux/systemd.md
Normal file
31
linux/systemd.md
Normal file
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
title: Systemd
|
||||
layout: default
|
||||
---
|
||||
|
||||
|
||||
Example with `kodi` : `/lib/systemd/system/kodi.service`
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description = Kodi Media Center
|
||||
After = remote-fs.target network-online.target
|
||||
Wants = network-online.target
|
||||
|
||||
[Service]
|
||||
User = pi
|
||||
Group = pi
|
||||
Type = simple
|
||||
ExecStart = /usr/bin/kodi-standalone
|
||||
; ExecStart = /usr/bin/su %i /usr/bin/kodi-standalone
|
||||
Restart = on-abort
|
||||
RestartSec = 5
|
||||
|
||||
[Install]
|
||||
WantedBy = multi-user.target
|
||||
```
|
||||
|
||||
```
|
||||
systemctl enable kodi
|
||||
systemctl start kodi
|
||||
```
|
211
programming/bridging.md
Normal file
211
programming/bridging.md
Normal file
|
@ -0,0 +1,211 @@
|
|||
---
|
||||
layout: default
|
||||
title: Bridging between languages
|
||||
categories: programming
|
||||
---
|
||||
We can cross call functions from languages that:
|
||||
|
||||
- Both are compilable → Strategy: Create a binary library and call function from it.
|
||||
- Library in `interpreted` and the project in `compiled` → Strategy: Create an interpreter context, then load the files and execute them.
|
||||
- Library in `compiled` and the project in `interpreted` → Strategy: Use `FFI`.
|
||||
- Both are interpreted
|
||||
- Probably give up. Bridging will be possible, but messy and complicated
|
||||
- Create two programs and exchange data between them using pipe, sockets, message quesues, databses etc.
|
||||
|
||||
### Creating shared and static library in Go
|
||||
|
||||
An example code that is shared [`example.go`](https://github.com/artur-gurgul/codebook):
|
||||
|
||||
``` go
|
||||
package main
|
||||
|
||||
import "C"
|
||||
import "fmt"
|
||||
|
||||
//export SayHello
|
||||
func SayHello(hello *C.char) {
|
||||
fmt.Print(C.GoString(hello))
|
||||
}
|
||||
|
||||
func main() {}
|
||||
```
|
||||
|
||||
- The `main` function is neccecery to include into library, because the final product has to have for example the GC rutines.
|
||||
- The comment starting from `//export {function name}` tells the comiler that this the function will be called from the outside.
|
||||
|
||||
#### Creating static library
|
||||
|
||||
```
|
||||
go build -o example.a -buildmode=c-archive example.go
|
||||
```
|
||||
|
||||
#### Creating dynamic library
|
||||
|
||||
```
|
||||
go build -o example.dylib -buildmode=c-shared example.go
|
||||
```
|
||||
|
||||
### Creating shared and static library in Swift
|
||||
|
||||
`point.swift`
|
||||
```swift point.swift
|
||||
public struct Point {
|
||||
public let x: Int
|
||||
public let y: Int
|
||||
|
||||
public init(x: Int, y: Int) {
|
||||
self.x = x
|
||||
self.y = y
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
and compile with command (module name is optional)
|
||||
|
||||
```sh
|
||||
swiftc point.swift -emit-module -module-name Point -emit-library -static
|
||||
```
|
||||
|
||||
**Using**
|
||||
|
||||
```swift
|
||||
import Point
|
||||
|
||||
let p = Point(x: 4, y: 20)
|
||||
print("Hello library!", p.x, p.y)
|
||||
```
|
||||
|
||||
compile with
|
||||
|
||||
```sh
|
||||
swiftc main.swift -L ./lib/ -I ./lib/ -lpoint
|
||||
```
|
||||
|
||||
#### Dynamic library in Swift
|
||||
|
||||
```
|
||||
swiftc point.swift -emit-module -emit-library
|
||||
```
|
||||
|
||||
it produces
|
||||
|
||||
- `libpoint.a`
|
||||
- `point.swiftdoc`
|
||||
- `point.swiftmodule`
|
||||
- `point.swiftsourceinfo`
|
||||
|
||||
Compile main program the same way as it has been down with the static one
|
||||
|
||||
Library searching paths `/usr/lib/`, `/usr/local/lib/`
|
||||
|
||||
**_Create package that emits library_**
|
||||
|
||||
```swift
|
||||
// swift-tools-version:5.3
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "MyLibrary",
|
||||
products: [
|
||||
/// type: automatic, based on the environment
|
||||
.library(name: "MyLibrary",
|
||||
// type: .dynamic, .static
|
||||
targets: ["MyLibrary"]
|
||||
),
|
||||
],
|
||||
targets: [
|
||||
.target(name: "MyLibrary", dependencies: []),
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
### Calling function from library in Go
|
||||
|
||||
First off we will create C++ library that we will use in out Go program.
|
||||
File `example.cxx`:
|
||||
|
||||
```c++
|
||||
#include <stdio.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
void PrintHello(const char* u) {
|
||||
printf("Hello: %s\n", u);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
And `example.hxx`:
|
||||
|
||||
```c++
|
||||
#pragma once
|
||||
void PrintHello(const char* u)
|
||||
```
|
||||
|
||||
`extern "C" {}` informs the compiler that we want the function names to be preserved. That is, to not "mangle" the names as is done for C++ code:
|
||||
|
||||
#### Creating static library
|
||||
|
||||
```bash
|
||||
clang++ -c -Wall -o lib.o ./example.cxx
|
||||
ar rc ./libexample.a ./lib.o
|
||||
```
|
||||
|
||||
#### Creating dynamic library
|
||||
|
||||
```bash
|
||||
clang++ -dynamiclib -o libexample.dylib example.cxx
|
||||
```
|
||||
|
||||
## Statically linking an example library in Go
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
// #cgo CFLAGS: -I.
|
||||
// #cgo LDFLAGS: -L. -lexample
|
||||
//
|
||||
// #include <example.hxx>
|
||||
import "C"
|
||||
|
||||
func main() {
|
||||
C.PrintHello(C.CString("Hello Golang"))
|
||||
}
|
||||
```
|
||||
|
||||
The program is linked staticaly with libexample when you build it.
|
||||
|
||||
#### Example of using library with FFI in Ruby
|
||||
|
||||
```shell
|
||||
gem install ffi
|
||||
```
|
||||
|
||||
```ruby
|
||||
require 'ffi'
|
||||
module Example
|
||||
extend FFI::Library
|
||||
ffi_lib './example.dylib'
|
||||
attach_function :SayHello, [:string]
|
||||
end
|
||||
```
|
||||
|
||||
```ruby
|
||||
Example.SayHello("Hello")
|
||||
```
|
||||
|
||||
More informations about FFI: [https://en.wikipedia.org/wiki/Foreign_function_interface](https://en.wikipedia.org/wiki/Foreign_function_interface)
|
||||
|
||||
#### Call shared library from Python
|
||||
|
||||
```python
|
||||
import ctypes
|
||||
libc = ctypes.CDLL('./example.dylib')
|
||||
libc.SayHello("Hello")
|
||||
```
|
||||
|
||||
## Interesting websites
|
||||
|
||||
- [https://blog.filippo.io/building-python-modules-with-go-1-5/](https://blog.filippo.io/building-python-modules-with-go-1-5/)
|
||||
- [https://id-rsa.pub/post/go15-calling-go-shared-libs-from-firefox-addon/](https://id-rsa.pub/post/go15-calling-go-shared-libs-from-firefox-addon/)
|
25
programming/solid.md
Normal file
25
programming/solid.md
Normal file
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
layout: default
|
||||
title: SOLID principles
|
||||
categories: programming
|
||||
---
|
||||
|
||||
### Single responsibility principle
|
||||
|
||||
This mean if the one thing in specification is changed you only need to change the code in the single place or class
|
||||
|
||||
### Open/closed principle
|
||||
|
||||
Software should be open for extension, but closed for modification.
|
||||
|
||||
### Liskov substitution principle
|
||||
|
||||
Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.
|
||||
|
||||
### Interface segregation principle
|
||||
|
||||
The interfaces should be small and provide as little methods as possible.
|
||||
|
||||
### Dependency inversion principle
|
||||
|
||||
The lower module must not depends on higher module. In order to achieve this the code must to relay on abstractions (in other words the code has to use interfaces/protocols)
|
62
utils/git-squash.md
Normal file
62
utils/git-squash.md
Normal file
|
@ -0,0 +1,62 @@
|
|||
---
|
||||
layout: default
|
||||
title: Git - notes
|
||||
categories: software
|
||||
---
|
||||
Squash the last three commits
|
||||
|
||||
```bash
|
||||
git rebase -i HEAD~3
|
||||
```
|
||||
|
||||
You will see something like this
|
||||
|
||||
```plain
|
||||
pick f392171 Added new feature X
|
||||
pick ba9dd9a Added new elements to page design
|
||||
pick df71a27 Updated CSS for new elements
|
||||
```
|
||||
|
||||
- `pick` `p` the commit will be taken
|
||||
- `squash` `s`, the commit will be blended with the above.
|
||||
|
||||
Edit to something like this
|
||||
|
||||
```plain
|
||||
pick f392171 New message for this three commit!
|
||||
squash ba9dd9a
|
||||
squash df71a27
|
||||
```
|
||||
|
||||
Now you can accept the change by continuing the rebase:
|
||||
|
||||
```bash
|
||||
git rebase --continue
|
||||
```
|
||||
|
||||
After that you need to force override the history using this command:
|
||||
|
||||
```bash
|
||||
git push origin master --force
|
||||
```
|
||||
|
||||
## Squash merge
|
||||
|
||||
```bash
|
||||
git checkout master
|
||||
git merge --squash bugfix
|
||||
git commit
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Checkout bare repository
|
||||
|
||||
```
|
||||
git clone --bare https://github.com/nextcloud/server.git /var/www/nextcloud.git --recursive
|
||||
|
||||
cd nextcloud.git; git worktree add /var/www/nextcloud
|
||||
cd /var/www/nextcloud; checkout -f v27.1.4
|
||||
|
||||
git --work-tree=/var/www/nextcloud --git dir=/home/artur/pub/drive/repos/nextcloud.git checkout -f v27.1.4 --recursive
|
||||
```
|
591
utils/recepies.yml
Normal file
591
utils/recepies.yml
Normal file
|
@ -0,0 +1,591 @@
|
|||
layout: default
|
||||
title: Recipes
|
||||
type: recipes
|
||||
|
||||
recipes:
|
||||
Read a DNS records:
|
||||
^: dns networking
|
||||
$: dig artgur.net +nostats +nocomments +nocmd
|
||||
|
||||
I/O Stats:
|
||||
$: iostat
|
||||
|
||||
Checking type of executable files:
|
||||
$: otool -hv test.so
|
||||
|
||||
Gzip of image:
|
||||
^: compression, zip
|
||||
$: dd if=/dev/sdb | gzip > ~/backup.img.gz
|
||||
|
||||
Progress with `dd`:
|
||||
$:
|
||||
- sudo dd if=2024-11-19-raspios-bookworm-armhf.img of=/dev/disk27 status=progress
|
||||
- sudo dd if=/dev/sdb | pv -s 5.29G | dd of=DriveCopy1.dd bs=4096
|
||||
- sudo pv ubuntu-24.04.1-desktop-amd64.iso | sudo dd of=/dev/disk27
|
||||
|
||||
Get directory size:
|
||||
$: du -sh MacOSBackup
|
||||
|
||||
Print all sizes in directory:
|
||||
$:
|
||||
- du -sh *
|
||||
- du -shc *
|
||||
|
||||
Compare two files:
|
||||
$:
|
||||
- vim -d file1 file2
|
||||
- mcdiff file1 file2
|
||||
|
||||
Disk manager:
|
||||
$: cfdisk /dev/sda
|
||||
|
||||
List of disks:
|
||||
$: parted -l
|
||||
|
||||
Informations about disk:
|
||||
$: fdisk -l /dev/sda
|
||||
|
||||
Power off the disk:
|
||||
$: udisksctl power-off -b /dev/sdX
|
||||
|
||||
Generate random password:
|
||||
$:
|
||||
- pwgen -s -1 32
|
||||
- openssl rand -hex 12
|
||||
|
||||
List block devices:
|
||||
$: lsblk
|
||||
|
||||
Linux headers:
|
||||
-
|
||||
$: uname -r
|
||||
-
|
||||
$: apt search linux-headers-$(uname -r)
|
||||
|
||||
Show all disks with json format:
|
||||
$: lsblk -J
|
||||
|
||||
List disk with uuid's:
|
||||
$: lsbkl -f
|
||||
|
||||
|
||||
vfat file system:
|
||||
- ➜: Create a file systyem
|
||||
$: mkfs.vfat -F 32 /dev/sdb4
|
||||
- ➜: Mount the file system
|
||||
$: mount -i -t vfat -oumask=0000,iocharset=utf8 /dev/sdb4 /root
|
||||
|
||||
See what processes are using the drive:
|
||||
$: lsof /where/drive/is/mounted
|
||||
|
||||
See what process opens the port:
|
||||
$:
|
||||
- lsof -p 6919
|
||||
- lsof -i :6919
|
||||
|
||||
See the stats of IO:
|
||||
$:
|
||||
- apt install sysstat iotop
|
||||
|
||||
- iostat -dh 2
|
||||
- iotop -o
|
||||
- sar -p -d -b 1
|
||||
- vmstat -d 1
|
||||
- vmstat -p /dev/sda2 1
|
||||
|
||||
Rsync:
|
||||
$: rsync -ah --progress /Volumes/Data /Volumes/Data\ 1/Junk/1TB\ Drive
|
||||
|
||||
reloading local DNS:
|
||||
$: sudo /etc/init.d/dns-clean start
|
||||
|
||||
Print all processes in json format:
|
||||
$: |
|
||||
ps aux | awk -v OFS=, '{print $1, $2}' | jq -R 'split(",") | {user: .[0], pid: .[1]}'
|
||||
|
||||
Split files:
|
||||
$: split -b 70M deno
|
||||
|
||||
Search and execute command from the history:
|
||||
$: eval `history | fzf | cut -s -d " " -f4-`
|
||||
|
||||
file: .zshrc
|
||||
content: |
|
||||
export HISTSIZE=100000000
|
||||
alias hexec='eval `history | fzf | cut -s -d " " -f4-`'
|
||||
|
||||
Editing command with editor:
|
||||
file: ~/.zshrc
|
||||
content: bindkey '^e' edit-command-line
|
||||
|
||||
Copy public ssh key:
|
||||
- cat ~/.ssh/id_rsa.pub | pbcopy
|
||||
|
||||
change password that was saved in a variable:
|
||||
$: echo "$archpass" | passwd "$archuser" --stdin
|
||||
|
||||
Git diff between branches:
|
||||
$: git diff release-1.2.0..release-1.2.1
|
||||
|
||||
MacOS info aliases in `.zhrc`:
|
||||
file: .zhrc
|
||||
content: |
|
||||
alias cpu='sysctl -n machdep.cpu.brand_string'
|
||||
alias cpu-temp='sudo powermetrics --samplers smc | grep -i "CPU die temperature"'
|
||||
alias gpu-temp='sudo powermetrics --samplers smc | grep -i "GPU die temperature"'
|
||||
alias lsusb='sudo ioreg -p IOUSB'
|
||||
alias allusb='ioreg -p IOUSB -w0 -l'
|
||||
|
||||
Power metrics:
|
||||
$: sudo powermetrics --samplers all
|
||||
|
||||
Install pods from non standard localisations:
|
||||
$: |
|
||||
pod 'WASHD', :git => 'https://github.com/vatlib/EasyUITextFields.git'
|
||||
pod 'WASHD', :path => '/Users/artur/projs/easyuitextfields'
|
||||
|
||||
SQLite select and search results with FZF:
|
||||
$: echo "select * from bookmarks" | sqlite3 bookmarks.db | fzf
|
||||
|
||||
Open file with FZF:
|
||||
$: nvim -o `fzf`
|
||||
|
||||
Set default shell. ZSH in this case:
|
||||
$: sudo chsh --shell /usr/bin/zsh user
|
||||
|
||||
Show Git object:
|
||||
$: pigz -d < .git/objects/02/f2cc93fee0b3cb7c9b75f49e4ded3f9b1480eb
|
||||
|
||||
list of wireless cards:
|
||||
$: lspci -knn | grep Net -A2
|
||||
|
||||
Scan networks:
|
||||
$: iwlist scan
|
||||
|
||||
Shutdown:
|
||||
$: shutdown -h now
|
||||
|
||||
Connect to the network:
|
||||
$: nmcli dev wifi connect TP-Link_5828 password my-secret-pass
|
||||
|
||||
You can forward port `80` to `8090`:
|
||||
$: iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8090
|
||||
|
||||
Allow accepting connections on `8090`:
|
||||
$: iptables -I INPUT -m tcp -p tcp --dport 8090 -j ACCEPT
|
||||
|
||||
Search files that contains particular string:
|
||||
$: grep -rnw "." -e "Search key"
|
||||
|
||||
Remove garbage files:
|
||||
$: find ./ -name ".DS_Store" -depth -exec rm {} \;
|
||||
|
||||
Find files, directories and symbolic links using regex:
|
||||
$: find ./ -iname `fo*` and `F??` -type f,d,l
|
||||
|
||||
Make text from pipe uppercased:
|
||||
$:
|
||||
- cat file.txt | tr [:lower:] [:upper:]
|
||||
- cat file.txt | tr [a-z] [A-Z]
|
||||
- tr [a-z] [A-Z] < linux.txt > output.txt
|
||||
|
||||
Installing packages in python for an user:
|
||||
$: pip3 install --user meson
|
||||
|
||||
|
||||
Calling module through interpreter:
|
||||
$: python3 -m pip install six
|
||||
|
||||
Remove spaces:
|
||||
$: cat file.txt | tr -d ' '
|
||||
|
||||
Remove duplicate characters:
|
||||
input-file: domains.txt
|
||||
content: |
|
||||
www.google.....com
|
||||
www.linkedin.com
|
||||
www.linuxsay.com
|
||||
|
||||
$: cat domains.txt | tr -s '.'
|
||||
output: |
|
||||
www.google.com
|
||||
www.linkedin.com
|
||||
|
||||
Extract digit:
|
||||
$:
|
||||
- echo "My UID is $UID" | tr -cd "[:digit:]\n"
|
||||
- echo "My UID is $UID" | tr -d "a-zA-Z"
|
||||
|
||||
Translate single character:
|
||||
$: echo "My UID is $UID" | tr " " "\n"
|
||||
|
||||
Get path by number:
|
||||
$: echo $PATH | cut -d ":" -f 1
|
||||
|
||||
list search path line by line:
|
||||
$: echo $PATH | tr ":" "\n"
|
||||
|
||||
Screen capture:
|
||||
$:
|
||||
- ffmpeg -f x11grab -video_size 1280x800 -framerate 25 -i $DISPLAY -c:v ffvhuff screen.mkv
|
||||
- ffmpeg -video_size 1280x800 -framerate 25 -f x11grab -i :0.0 -f pulse -ac 2. \
|
||||
-i default -vcodec vp8 -acodec libvorbis myvideo_$(date +%d_%B_%Y_%H:%M).webm
|
||||
|
||||
Take a screenshot:
|
||||
$:
|
||||
- xwd -root -out screenshot.xwd
|
||||
- maim -s -u | xclip -selection clipboard -t image/png -i
|
||||
- imlib2_grab screenshot.png
|
||||
|
||||
Install Python package for the user:
|
||||
$: python3 -m pip install --user pyelftools
|
||||
|
||||
Erase free space:
|
||||
$: sudo diskutil secureErase freespace 1 /Volumes/Data\ Drive
|
||||
|
||||
Format disk:
|
||||
$: sudo diskutil eraseDisk ExFAT data /dev/disk26
|
||||
|
||||
Search for commit:
|
||||
$: alias gf='git log --all --oneline | fzf'
|
||||
|
||||
Converts all files in current directory revursevely:
|
||||
install: brew install imagemagick
|
||||
tags: convert image-magic
|
||||
$: alias rmalfa='find . -name “*.png” -exec convert “{}” -alpha off “{}” \;'
|
||||
|
||||
Weather alias:
|
||||
$: alias weather='curl wttr.in'
|
||||
|
||||
Starting an electron app on wayland:
|
||||
|
||||
- ➜: Start chromium using wayland
|
||||
$: chromium --enable-features=UseOzonePlatform --ozone-platform=wayland
|
||||
|
||||
- ➜: The same for electron-based apps
|
||||
$: |
|
||||
`app-executable` --enable-features=UseOzonePlatform \
|
||||
--ozone-platform=wayland
|
||||
|
||||
Save website As PDF:
|
||||
file: .zshrc
|
||||
content: |
|
||||
function aspdf {
|
||||
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --headless --print-to-pdf="./$1.pdf" $2
|
||||
}
|
||||
|
||||
$: aspdf "filename" "https://domain.com/example.pdf"
|
||||
|
||||
Export Markdown as PDF:
|
||||
$:
|
||||
- pandoc README.md -o README.pdf
|
||||
- pandoc --from=gfm --to=pdf -o README.pdf README.md
|
||||
|
||||
Gem path:
|
||||
content: |
|
||||
export GEM_HOME=$HOME/.gem
|
||||
path=("$GEM_HOME/bin" $path)
|
||||
|
||||
QEMU - port forwarding:
|
||||
$: |
|
||||
qemu-system-i386 -net nic,model=rtl8139 \
|
||||
-net user,hostfwd=tcp::3389-:3389,hostfwd=tcp::443-:443,hostfwd=tcp::992-:992
|
||||
|
||||
SQL using regex:
|
||||
description: Add a check constraint to the `id` column to enforce alphanumeric strings of exactly 5 characters long
|
||||
$: ALTER TABLE short_urls ADD CONSTRAINT id CHECK (id ~ '^[a-zA-Z0-9]{5}$');
|
||||
|
||||
Console font size:
|
||||
description: Edit file → `/etc/default/console-setup`
|
||||
$: dpkg-reconfigure -plow console-setup
|
||||
|
||||
Redirect errors to null device:
|
||||
$: find / 2>/dev/null
|
||||
|
||||
Installing nonfree firmware from repository:
|
||||
description: |
|
||||
I.e: Firmware for nonfree driver for Intel's WIFI cards.
|
||||
https://packages.debian.org/sid/firmware-iwlwifi
|
||||
$: apt-get update && apt-get install firmware-linux-nonfree
|
||||
|
||||
Installing nonfree firmware from manufacturer:
|
||||
|
||||
- ➜: Search for binary. An example
|
||||
link: https://www.intel.com/content/www/us/en/support/articles/000005511/wireless.html
|
||||
|
||||
- ➜: Extract and copy like
|
||||
$: cp iwlwifi-cc-a0-46.ucode /lib/firmware
|
||||
|
||||
Linux - RAM disk:
|
||||
|
||||
description: This might be useful for spead up programs that heavily use disk
|
||||
$: mount -t TYPE -o size=SIZE FSTYPE MOUNTPOINT
|
||||
info: |
|
||||
* `TYPE` → either `tmpfs` or `ramfs`.
|
||||
* `SIZE` → ie. `512m`
|
||||
* `FSTYPE` → File system type, either `tmpfs`, `ramfs`, `ext4`, etc.
|
||||
|
||||
file: /etc/fstab
|
||||
content: tmpfs /mnt/ramdisk tmpfs nodev,nosuid,noexec,nodiratime,size=1024M 0 0
|
||||
|
||||
fstab:
|
||||
➜: Use 'blkid' to print the universally unique identifiers, and can be used in fstab file like
|
||||
file: /etc/fstab
|
||||
|
||||
content: |
|
||||
# <file system> <mount point> <type> <options> <dump> <pass>
|
||||
|
||||
UUID=1a38b8ca-e1f5-45e6-bbe8-3abd2775b3a6 / ext4 errors=remount-ro 0 1
|
||||
/swapfile none swap sw 0 0
|
||||
/dev/disk/by-uuid/4D3C-4E36 /mnt/4D3C-4E36 auto nosuid,nodev,nofail,x-gvfs-show 0 0
|
||||
|
||||
UUID=e21eebe4-471a-4375-8c4c-618b3733a940 /home ext4 nodev,nosuid 0 2
|
||||
|
||||
|
||||
Linux - Mount disk from `qcow2` image:
|
||||
|
||||
- ➜: Step 1 - Enable NBD on the Host
|
||||
$: modprobe nbd max_part=8
|
||||
|
||||
- ➜: Step 2 - Connect the QCOW2 as network block device
|
||||
$: qemu-nbd --connect=/dev/nbd0 /var/lib/vz/images/100/vm-100-disk-1.qcow2
|
||||
|
||||
- ➜: Step 3 - Find The Virtual Machine Partitions
|
||||
$: fdisk /dev/nbd0 -l
|
||||
|
||||
- ➜: Step 4 - Mount the partition from the VM
|
||||
$: mount /dev/nbd0p1 /mnt/somepoint/
|
||||
|
||||
- ➜: Step 5 - After you done, unmount and disconnect
|
||||
$:
|
||||
- umount /mnt/somepoint/
|
||||
- qemu-nbd --disconnect /dev/nbd0
|
||||
- rmmod nbd
|
||||
|
||||
Ubuntu - Power management - make Ubuntu do nothing when laptop lid is closed:
|
||||
|
||||
- ➜: Open the `/etc/systemd/logind.conf` file in a text editor as root, for example
|
||||
$: sudo -H gedit /etc/systemd/logind.conf
|
||||
|
||||
- ➜: If `HandleLidSwitch` is not set to ignore then change it
|
||||
$: HandleLidSwitch=ignore
|
||||
note: |
|
||||
Other settings that the action can be ignored: `HandleLidSwitchExternalPower`, `HandleLidSwitchDocked`, `IdleAction`.
|
||||
|
||||
- ➜: Restart the systemd daemon (be aware that this command will log you out)
|
||||
$:
|
||||
- sudo systemctl restart systemd-logind
|
||||
- sudo service systemd-logind restart
|
||||
|
||||
Chroot environment of Debian sid:
|
||||
|
||||
- ➜: Install Bootstrap
|
||||
$: sudo apt install debootstrap
|
||||
|
||||
- ➜: Create a directory that you want to use for the base system (_chroot-debian_ in this case)
|
||||
$: mkdir chroot-debian
|
||||
|
||||
- ➜: Create a base system
|
||||
$: sudo debootstrap sid chroot-debian http://deb.debian.org/debian
|
||||
note: Valid names `sid`, `stable` or any debian code name
|
||||
|
||||
- ➜: Mount filesystems
|
||||
$:
|
||||
- sudo mount -o bind /dev chroot-debian/dev
|
||||
- sudo mount -t sysfs none chroot-debian/sys
|
||||
- sudo mount -o bind /proc chroot-debian/proc
|
||||
|
||||
- ➜: Optionally, copy DNS resolver configuration.
|
||||
$: sudo cp /etc/resolv.conf /path/to/chroot-env/etc/resolv.conf
|
||||
|
||||
- ➜: Start chrooting
|
||||
$: sudo chroot chroot-debian /bin/bash
|
||||
|
||||
- ➜: Once done, exit the session and unmount
|
||||
$: sudo umount chroot-debian/dev chroot-debian/proc
|
||||
|
||||
Pass variables to chrooted environment:
|
||||
$: chroot ./ env -i PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
|
||||
Allowing user to run a command as root:
|
||||
$: sudo visudo
|
||||
content: artur ALL=(ALL) chroot /path/to/chroot-env
|
||||
|
||||
|
||||
Nginx - serving files setup DAV:
|
||||
|
||||
- ➜: Full version with 3rd party extensions
|
||||
$: apt install nginx-full nginx-extras
|
||||
|
||||
-
|
||||
content: |
|
||||
location / {
|
||||
index nonextistent;
|
||||
autoindex on;
|
||||
autoindex_format json;
|
||||
}
|
||||
|
||||
location /restricted {
|
||||
fancyindex on;
|
||||
fancyindex_exact_size off;
|
||||
auth_basic "Restricted";
|
||||
auth_basic_user_file "/etc/nginx/.htpasswd";
|
||||
}
|
||||
|
||||
location /dropbox {
|
||||
index nonextistent;
|
||||
autoindex on;
|
||||
autoindex_format json;
|
||||
|
||||
dav_methods PUT DELETE MKCOL COPY MOVE;
|
||||
dav_ext_methods PROPFIND OPTIONS LOCK UNLOCK;
|
||||
dav_access user:rw group:r all:r;
|
||||
|
||||
# client_max_body_size 0;
|
||||
create_full_put_path on;
|
||||
client_body_temp_path /tmp/;
|
||||
|
||||
limit_except GET PROPFIND OPTIONS HEAD {
|
||||
auth_basic "Restricted";
|
||||
auth_basic_user_file "/etc/nginx/.htpasswd";
|
||||
}
|
||||
# auth_pam "Restricted";
|
||||
# auth_pam_service_name "common-auth";
|
||||
}
|
||||
|
||||
- ➜: Create password
|
||||
$:
|
||||
- echo -n 'sammy:' >> /etc/nginx/.htpasswd
|
||||
- openssl passwd -apr1 >> /etc/nginx/.htpasswd
|
||||
|
||||
- ➜: Another method to set the password
|
||||
$:
|
||||
- htpasswd -c /etc/nginx/.htpasswd sammy
|
||||
- htpasswd /etc/nginx/.htpasswd another_user
|
||||
note: UI Client `Cyberduck`
|
||||
|
||||
Backing up the entire OS:
|
||||
$: sudo rsync -aAXHv / --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found"} /mnt
|
||||
|
||||
|
||||
Python:
|
||||
$: apt install python3-full
|
||||
|
||||
Check battery:
|
||||
- $:
|
||||
- upower -i /org/freedesktop/UPower/devices/battery_BAT0
|
||||
- upower -i `upower -e | grep 'BAT'`
|
||||
- upower -i $(upower -e | grep BAT) | grep --color=never -E "state|to\ full|to\ empty|percentage"
|
||||
|
||||
- ➜: Battery capacity
|
||||
$: cat /sys/class/power_supply/BAT0/capacity
|
||||
|
||||
- ➜: ACPI
|
||||
$:
|
||||
- apt install acpi
|
||||
- acpi -V
|
||||
- acpi -t
|
||||
|
||||
- ➜: Watch the status for example
|
||||
$: watch --interval=5 acpi -V
|
||||
|
||||
How to scan open ports:
|
||||
$:
|
||||
- nmap -sT -p- 10.10.8.8
|
||||
- nmap -p 80 127.0.0.1
|
||||
|
||||
Remove non ASCII characters form the file:
|
||||
$: sed 's/[^[:print:]\t]//g' script.sh > cleaned_script.sh
|
||||
|
||||
Network Manager:
|
||||
|
||||
- ➜: Check Status
|
||||
$:
|
||||
- pacman -S networkmanager
|
||||
- systemctl status NetworkManager
|
||||
|
||||
- $:
|
||||
- nmcli dev status
|
||||
- nmcli radio wifi
|
||||
|
||||
- $: nmcli radio wifi on
|
||||
- $: nmcli dev wifi list
|
||||
- $: nmcli dev wifi connect network-ssid
|
||||
- $: nmcli dev wifi connect network-ssid password "network-password"
|
||||
|
||||
- ➜: With password promot
|
||||
$: nmcli dev wifi connect network-ssid password "network-password"
|
||||
|
||||
- $: nmcli con show
|
||||
|
||||
- $:
|
||||
- nmcli con down ssid/uuid
|
||||
- nmcli con up ssid/uuid
|
||||
|
||||
- $: ip link set eno1 up
|
||||
|
||||
DHCP Client:
|
||||
$: pacman -S dhclient dhcpcd
|
||||
|
||||
Turn dchp on for the interface:
|
||||
$: dhclient eth0 -v
|
||||
|
||||
Create file:
|
||||
|
||||
content: |
|
||||
cat > filename <<- "EOF"
|
||||
file contents
|
||||
more contents
|
||||
EOF
|
||||
|
||||
|
||||
Get data about file include number of links:
|
||||
$: stat f1
|
||||
|
||||
File decryption:
|
||||
$: openssl aes256 -md sha256 -d -in file.enc.zip -out file.zip -pass pass:"<password>"
|
||||
|
||||
Base64:
|
||||
$:
|
||||
- openssl base64 -in qrcode.png -out qrcode.png.base64
|
||||
- openssl base64 -in qrcode.png
|
||||
|
||||
Extract tar.xz:
|
||||
$: tar -xJf file.tar.xz -C destination
|
||||
|
||||
Execute command:
|
||||
|
||||
- ➜: Execute `ls` for each each object in current directory that starts from `D` or `M`
|
||||
$: ls {D*,M*}
|
||||
|
||||
- ➜: Utworzenie dwóch katalogów `test_a`, `test_b`
|
||||
$: mkdir test_{a,b}
|
||||
|
||||
Copy content of directory and merge it. Dereference links:
|
||||
$: cp -LTr /form/ .
|
||||
|
||||
JSON Linux apis:
|
||||
$:
|
||||
- tree -J
|
||||
- ls -l | jq -R -s -c 'split("\n")[:-1]'
|
||||
|
||||
List dependencies:
|
||||
$:
|
||||
- pacman -Sy lld
|
||||
- llvm-nm
|
||||
|
||||
Find location of executable:
|
||||
$: type -a python
|
||||
|
||||
Execute Command when file is changed:
|
||||
-
|
||||
$: brew install fswatch
|
||||
-
|
||||
$: fswatch -o . --exclude "\.build.*$" | xargs -n1 -I{} your-command
|
||||
|
||||
Add capabilities:
|
||||
description: allow all to open 443 port
|
||||
$: sudo setcap 'cap_net_bind_service=+ep' ./service
|
||||
|
||||
Test speed of the drive:
|
||||
$: sudo hdparam -t --direct /dev/mmcblk0
|
941
utils/recepiesmd.md
Normal file
941
utils/recepiesmd.md
Normal file
|
@ -0,0 +1,941 @@
|
|||
---
|
||||
layout: default
|
||||
title: Recipes MD
|
||||
---
|
||||
|
||||
#### Read a DNS records
|
||||
|
||||
dig artgur.net +nostats +nocomments +nocmd
|
||||
|
||||
#### I/O Stats
|
||||
|
||||
iostat
|
||||
|
||||
#### Checking type of executable files
|
||||
|
||||
otool -hv test.so
|
||||
|
||||
#### #Receipe Gzip of image
|
||||
|
||||
```shell
|
||||
dd if=/dev/sdb | gzip > ~/backup.img.gz
|
||||
```
|
||||
|
||||
#### Progress with `dd`
|
||||
|
||||
```
|
||||
sudo dd if=2024-11-19-raspios-bookworm-armhf.img of=/dev/disk27 status=progress
|
||||
sudo dd if=/dev/sdb | pv -s 5.29G | dd of=DriveCopy1.dd bs=4096
|
||||
sudo pv ubuntu-24.04.1-desktop-amd64.iso | sudo dd of=/dev/disk27
|
||||
```
|
||||
|
||||
#### Get directory size
|
||||
|
||||
du -sh MacOSBackup
|
||||
|
||||
##### Print all sizes in directory
|
||||
```bash
|
||||
du -sh *
|
||||
```
|
||||
|
||||
```bash
|
||||
du -shc *
|
||||
```
|
||||
|
||||
#### Compare two files
|
||||
|
||||
```bash
|
||||
vim -d file1 file2
|
||||
mcdiff file1 file2
|
||||
```
|
||||
|
||||
### Disk manager
|
||||
|
||||
```bash
|
||||
cfdisk /dev/sda
|
||||
```
|
||||
|
||||
#### List disks
|
||||
|
||||
```
|
||||
parted -l
|
||||
```
|
||||
|
||||
#### informations about disk
|
||||
|
||||
```bash
|
||||
fdisk -l /dev/sda
|
||||
```
|
||||
|
||||
#### Power off the disk
|
||||
|
||||
```
|
||||
udisksctl power-off -b /dev/sdX
|
||||
```
|
||||
#### Generate random password
|
||||
|
||||
```bash
|
||||
pwgen -s -1 32
|
||||
```
|
||||
or
|
||||
```
|
||||
openssl rand -hex 12
|
||||
```
|
||||
#### List block devices
|
||||
|
||||
lsblk
|
||||
|
||||
#### Linux headers
|
||||
|
||||
```
|
||||
uname -r
|
||||
apt search linux-headers-$(uname -r)
|
||||
```
|
||||
|
||||
Show all disks with json format
|
||||
```shell
|
||||
lsblk -J
|
||||
```
|
||||
List disk with uuid's
|
||||
```shell
|
||||
lsbkl -f
|
||||
```
|
||||
|
||||
|
||||
#### MKFS
|
||||
|
||||
```bash
|
||||
mkfs.vfat -F 32 /dev/sdb4
|
||||
```
|
||||
|
||||
```bash
|
||||
mount -i -t vfat -oumask=0000,iocharset=utf8 /dev/sdb4 /root
|
||||
```
|
||||
|
||||
#### See what processes are using the drive
|
||||
|
||||
```
|
||||
lsof /where/drive/is/mounted
|
||||
```
|
||||
|
||||
### See what process opens the port
|
||||
|
||||
```bash
|
||||
lsof -p 6919
|
||||
lsof -i :6919
|
||||
```
|
||||
#### See the stats of IO
|
||||
|
||||
```
|
||||
apt install sysstat iotop
|
||||
```
|
||||
|
||||
|
||||
```
|
||||
iostat -dh 2
|
||||
iotop -o
|
||||
sar -p -d -b 1
|
||||
vmstat -d 1
|
||||
vmstat -p /dev/sda2 1
|
||||
```
|
||||
|
||||
### Rsync
|
||||
|
||||
```
|
||||
rsync -ah --progress /Volumes/Data /Volumes/Data\ 1/Junk/1TB\ Drive
|
||||
```
|
||||
|
||||
|
||||
#### reloading local DNS
|
||||
|
||||
```shell
|
||||
sudo /etc/init.d/dns-clean start
|
||||
```
|
||||
|
||||
#### Print all processes in json format
|
||||
|
||||
``` shell
|
||||
ps aux |
|
||||
awk -v OFS=, '{print $1, $2}' |
|
||||
jq -R 'split(",") | {user: .[0], pid: .[1]}'
|
||||
```
|
||||
|
||||
### Split files
|
||||
|
||||
```
|
||||
split -b 70M deno
|
||||
```
|
||||
|
||||
#### Search and execute command from the history
|
||||
|
||||
```bash
|
||||
eval `history | fzf | cut -s -d " " -f4-`
|
||||
```
|
||||
|
||||
Adding this to `.zshrc`
|
||||
|
||||
```
|
||||
export HISTSIZE=100000000
|
||||
alias hexec='eval `history | fzf | cut -s -d " " -f4-`'
|
||||
```
|
||||
#### Editing command with editor
|
||||
|
||||
`~/.zshrc`
|
||||
|
||||
```
|
||||
bindkey '^e' edit-command-line
|
||||
```
|
||||
#### Copy public ssh key
|
||||
|
||||
```bash
|
||||
cat ~/.ssh/id_rsa.pub | pbcopy
|
||||
```
|
||||
|
||||
#### change password that was saved in a variable
|
||||
|
||||
```bash
|
||||
cho "$archpass" | passwd "$archuser" --stdin
|
||||
```
|
||||
#### Git diff between branches
|
||||
|
||||
git diff release-1.2.0..release-1.2.1
|
||||
|
||||
|
||||
#### MacOS info aliases in`.zhrc`
|
||||
|
||||
```
|
||||
alias cpu='sysctl -n machdep.cpu.brand_string'
|
||||
alias cpu-temp='sudo powermetrics --samplers smc | grep -i "CPU die temperature"'
|
||||
alias gpu-temp='sudo powermetrics --samplers smc | grep -i "GPU die temperature"'
|
||||
alias lsusb='sudo ioreg -p IOUSB'
|
||||
alias allusb='ioreg -p IOUSB -w0 -l'
|
||||
```
|
||||
|
||||
**Power metrics**
|
||||
|
||||
```bash
|
||||
sudo powermetrics --samplers all
|
||||
```
|
||||
#### Install pods from non standard localisations
|
||||
|
||||
```ruby
|
||||
pod 'WASHD', :git => 'https://github.com/vatlib/EasyUITextFields.git'
|
||||
pod 'WASHD', :path => '/Users/artur/projs/easyuitextfields'
|
||||
```
|
||||
|
||||
#### SQLite select and search results with FZF
|
||||
|
||||
```bash
|
||||
echo "select * from bookmarks" | sqlite3 bookmarks.db | fzf
|
||||
```
|
||||
|
||||
#### Open file with FZF
|
||||
|
||||
```bash
|
||||
nvim -o `fzf`
|
||||
```
|
||||
|
||||
#### Set default shell. ZSH in this case
|
||||
|
||||
sudo chsh --shell /usr/bin/zsh user
|
||||
|
||||
#### Show Git object
|
||||
```sh
|
||||
pigz -d < .git/objects/02/f2cc93fee0b3cb7c9b75f49e4ded3f9b1480eb
|
||||
```
|
||||
|
||||
#### list of wireless cards
|
||||
|
||||
lspci -knn | grep Net -A2
|
||||
|
||||
#### Scan networks
|
||||
|
||||
iwlist scan
|
||||
|
||||
#### Shutdown
|
||||
|
||||
shutdown -h now
|
||||
|
||||
#### Connect to the network
|
||||
|
||||
nmcli dev wifi connect TP-Link_5828 password my-secret-pass
|
||||
|
||||
#### You can forward port `80` to `8090`
|
||||
|
||||
```shell
|
||||
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8090
|
||||
```
|
||||
|
||||
#### Allow accepting connections on `8090`
|
||||
|
||||
```shell
|
||||
iptables -I INPUT -m tcp -p tcp --dport 8090 -j ACCEPT
|
||||
```
|
||||
#### Search files that contains particular string
|
||||
|
||||
``` shell
|
||||
grep -rnw "." -e "Search key"
|
||||
```
|
||||
|
||||
#### Remove garbage files
|
||||
|
||||
find ./ -name ".DS_Store" -depth -exec rm {} \;
|
||||
|
||||
#### Find files, directories and symbolic links using regex
|
||||
|
||||
find ./ -iname `fo*` and `F??` -type f,d,l
|
||||
|
||||
#### Make text from pipe uppercased
|
||||
|
||||
```bash
|
||||
cat file.txt | tr [:lower:] [:upper:]
|
||||
cat file.txt | tr [a-z] [A-Z]
|
||||
tr [a-z] [A-Z] < linux.txt > output.txt
|
||||
```
|
||||
|
||||
#### Installing packages for python
|
||||
|
||||
**_just for user_**
|
||||
|
||||
```bash
|
||||
pip3 install --user meson
|
||||
```
|
||||
|
||||
**_calling module through interpreter_**
|
||||
|
||||
```bash
|
||||
python3 -m pip install six
|
||||
```
|
||||
|
||||
#### Remove spaces
|
||||
|
||||
```bash
|
||||
cat file.txt | tr -d ' '
|
||||
```
|
||||
|
||||
#### Remove duplicate characters
|
||||
|
||||
|
||||
```bash
|
||||
$ cat domains.txt
|
||||
|
||||
www.google.....com
|
||||
www.linkedin.com
|
||||
www.linuxsay.com
|
||||
```
|
||||
|
||||
```bash
|
||||
$ cat domains.txt | tr -s '.'
|
||||
|
||||
www.google.com
|
||||
www.linkedin.com
|
||||
```
|
||||
|
||||
#### Extract digit
|
||||
|
||||
``` bash
|
||||
echo "My UID is $UID" | tr -cd "[:digit:]\n"
|
||||
echo "My UID is $UID" | tr -d "a-zA-Z"
|
||||
```
|
||||
|
||||
#### Translate single character
|
||||
|
||||
```bash
|
||||
echo "My UID is $UID" | tr " " "\n"
|
||||
```
|
||||
|
||||
#### Get path by number
|
||||
|
||||
```bash
|
||||
echo $PATH | cut -d ":" -f 1
|
||||
```
|
||||
|
||||
#### list search path line by line
|
||||
|
||||
```bash
|
||||
echo $PATH | tr ":" "\n"
|
||||
```
|
||||
|
||||
#### Screen capture
|
||||
|
||||
```
|
||||
ffmpeg -f x11grab -video_size 1280x800 -framerate 25 -i $DISPLAY -c:v ffvhuff screen.mkv
|
||||
|
||||
ffmpeg -video_size 1280x800 -framerate 25 -f x11grab -i :0.0 -f pulse -ac 2. \
|
||||
-i default -vcodec vp8 -acodec libvorbis myvideo_$(date +%d_%B_%Y_%H:%M).webm
|
||||
```
|
||||
|
||||
#### Take a screenshot
|
||||
|
||||
```bash
|
||||
xwd -root -out screenshot.xwd
|
||||
maim -s -u | xclip -selection clipboard -t image/png -i
|
||||
imlib2_grab screenshot.png
|
||||
```
|
||||
|
||||
#### Install Python package for the user
|
||||
|
||||
python3 -m pip install --user pyelftools
|
||||
|
||||
#### Erase free space
|
||||
|
||||
sudo diskutil secureErase freespace 1 /Volumes/Data\ Drive
|
||||
|
||||
#### Format disk
|
||||
|
||||
sudo diskutil eraseDisk ExFAT data /dev/disk26
|
||||
#### Search for commit
|
||||
|
||||
```bash
|
||||
alias gf='git log --all --oneline | fzf'
|
||||
```
|
||||
|
||||
#### Remove alpha channel from all files
|
||||
|
||||
```bash
|
||||
# ➜ brew install imagemagick
|
||||
```
|
||||
|
||||
**Converts all files in current directory revursevely**
|
||||
|
||||
```bash
|
||||
alias rmalfa='find . -name “*.png” -exec convert “{}” -alpha off “{}” \;'
|
||||
```
|
||||
|
||||
#### Weather alias
|
||||
|
||||
```sh
|
||||
alias weather='curl wttr.in'
|
||||
```
|
||||
|
||||
#### Starting an electron app on wayland
|
||||
|
||||
Start chromium using wayland
|
||||
|
||||
```bash
|
||||
chromium --enable-features=UseOzonePlatform --ozone-platform=wayland
|
||||
```
|
||||
|
||||
It’s the same for electron-based apps:
|
||||
|
||||
```bash
|
||||
`app-executable` --enable-features=UseOzonePlatform \
|
||||
--ozone-platform=wayland
|
||||
```
|
||||
|
||||
#### Save website As PDF
|
||||
|
||||
```bash
|
||||
function aspdf {
|
||||
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --headless --print-to-pdf="./$1.pdf" $2
|
||||
}
|
||||
```
|
||||
|
||||
Usage
|
||||
|
||||
```bash
|
||||
aspdf "filename" "https://superuser.com/questions/592974/how-to-print-to-save-as-pdf-from-a-command-line-with-chrome-or-chromium"
|
||||
```
|
||||
|
||||
### Export Markdown as PDF
|
||||
|
||||
```bash
|
||||
pandoc README.md -o README.pdf
|
||||
```
|
||||
|
||||
```bash
|
||||
pandoc --from=gfm --to=pdf -o README.pdf README.md
|
||||
```
|
||||
|
||||
#### Gem path
|
||||
|
||||
```hash
|
||||
export GEM_HOME=$HOME/.gem
|
||||
path=("$GEM_HOME/bin" $path)
|
||||
```
|
||||
|
||||
#### QEMU - port forwarding
|
||||
|
||||
```bash
|
||||
qemu-system-i386 -net nic,model=rtl8139 \
|
||||
-net user,hostfwd=tcp::3389-:3389,hostfwd=tcp::443-:443,hostfwd=tcp::992-:992
|
||||
```
|
||||
|
||||
#### SQL using regex
|
||||
Add a check constraint to the `id` column to enforce alphanumeric strings of exactly 5 characters long
|
||||
|
||||
```sql
|
||||
ALTER TABLE short_urls ADD CONSTRAINT id CHECK (id ~ '^[a-zA-Z0-9]{5}$');
|
||||
```
|
||||
|
||||
#### Console font size
|
||||
|
||||
Edit file → `/etc/default/console-setup`
|
||||
|
||||
```bash
|
||||
dpkg-reconfigure -plow console-setup
|
||||
```
|
||||
|
||||
#### Redirect errors to null device
|
||||
|
||||
find / 2>/dev/null
|
||||
|
||||
#### Installing nonfree firmware from repository
|
||||
|
||||
I.e: Firmware for nonfree driver for Intel's WIFI cards.
|
||||
|
||||
```
|
||||
https://packages.debian.org/sid/firmware-iwlwifi
|
||||
```
|
||||
|
||||
```bash
|
||||
apt-get update && apt-get install firmware-linux-nonfree
|
||||
```
|
||||
|
||||
#### Installing nonfree firmware from manufacturer
|
||||
|
||||
Search for binary. An example:
|
||||
|
||||
[https://www.intel.com/content/www/us/en/support/articles/000005511/wireless.html](https://www.intel.com/content/www/us/en/support/articles/000005511/wireless.html)
|
||||
|
||||
Extract and copy like
|
||||
|
||||
```bash
|
||||
cp iwlwifi-cc-a0-46.ucode /lib/firmware
|
||||
```
|
||||
|
||||
### Linux - RAM disk
|
||||
|
||||
This might be useful for spead up programs that heavily use disk.
|
||||
|
||||
```
|
||||
mount -t TYPE -o size=SIZE FSTYPE MOUNTPOINT
|
||||
```
|
||||
* `TYPE` → either `tmpfs` or `ramfs`.
|
||||
* `SIZE` → ie. `512m`
|
||||
* `FSTYPE` → File system type, either `tmpfs`, `ramfs`, `ext4`, etc.
|
||||
|
||||
To make this setting persistent you might want to add to `/etc/fstab` fallowing line
|
||||
|
||||
```plain
|
||||
tmpfs /mnt/ramdisk tmpfs nodev,nosuid,noexec,nodiratime,size=1024M 0 0
|
||||
```
|
||||
|
||||
#### fstab
|
||||
|
||||
Use 'blkid' to print the universally unique identifiers, and can be used in fstab file like
|
||||
|
||||
```
|
||||
# <file system> <mount point> <type> <options> <dump> <pass>
|
||||
|
||||
UUID=1a38b8ca-e1f5-45e6-bbe8-3abd2775b3a6 / ext4 errors=remount-ro 0 1
|
||||
/swapfile none swap sw 0 0
|
||||
/dev/disk/by-uuid/4D3C-4E36 /mnt/4D3C-4E36 auto nosuid,nodev,nofail,x-gvfs-show 0 0
|
||||
|
||||
UUID=e21eebe4-471a-4375-8c4c-618b3733a940 /home ext4 nodev,nosuid 0 2
|
||||
```
|
||||
|
||||
|
||||
### Linux - Mount disk from `qcow2` image
|
||||
|
||||
|
||||
Step 1 - Enable NBD on the Host
|
||||
|
||||
```bash
|
||||
modprobe nbd max_part=8
|
||||
```
|
||||
|
||||
Step 2 - Connect the QCOW2 as network block device
|
||||
|
||||
```bash
|
||||
qemu-nbd --connect=/dev/nbd0 /var/lib/vz/images/100/vm-100-disk-1.qcow2
|
||||
```
|
||||
|
||||
Step 3 - Find The Virtual Machine Partitions
|
||||
|
||||
```bash
|
||||
fdisk /dev/nbd0 -l
|
||||
```
|
||||
|
||||
Step 4 - Mount the partition from the VM
|
||||
|
||||
```bash
|
||||
mount /dev/nbd0p1 /mnt/somepoint/
|
||||
```
|
||||
|
||||
Step 5 - After you done, unmount and disconnect
|
||||
|
||||
```bash
|
||||
umount /mnt/somepoint/
|
||||
qemu-nbd --disconnect /dev/nbd0
|
||||
rmmod nbd
|
||||
```
|
||||
|
||||
### Ubuntu - Power management
|
||||
|
||||
To make Ubuntu do nothing when laptop lid is closed:
|
||||
|
||||
From For 13.10 onwards:
|
||||
|
||||
Open the `/etc/systemd/logind.conf` file in a text editor as root, for example:
|
||||
|
||||
```bash
|
||||
sudo -H gedit /etc/systemd/logind.conf
|
||||
```
|
||||
|
||||
If `HandleLidSwitch` is not set to ignore then change it:
|
||||
|
||||
```bash
|
||||
HandleLidSwitch=ignore
|
||||
```
|
||||
|
||||
Other settings that the action can be ignored: `HandleLidSwitchExternalPower`, `HandleLidSwitchDocked`, `IdleAction`.
|
||||
|
||||
Restart the systemd daemon (be aware that this command will log you out):
|
||||
|
||||
```bash
|
||||
sudo systemctl restart systemd-logind
|
||||
```
|
||||
|
||||
or, from 15.04 onwards:
|
||||
|
||||
```bash
|
||||
sudo service systemd-logind restart
|
||||
```
|
||||
|
||||
### Chroot environment of Debian sid
|
||||
|
||||
Install Bootstrap
|
||||
|
||||
```bash
|
||||
sudo apt install debootstrap
|
||||
```
|
||||
|
||||
Create a directory that you want to use for the base system (_chroot-debian_ in this case)
|
||||
|
||||
```bash
|
||||
mkdir chroot-debian
|
||||
```
|
||||
|
||||
Create a base system
|
||||
|
||||
```bash
|
||||
sudo debootstrap sid chroot-debian http://deb.debian.org/debian
|
||||
```
|
||||
|
||||
Valid names `sid`, `stable` or any debian code name
|
||||
|
||||
Mount filesystems
|
||||
|
||||
```bash
|
||||
sudo mount -o bind /dev chroot-debian/dev
|
||||
sudo mount -t sysfs none chroot-debian/sys
|
||||
sudo mount -o bind /proc chroot-debian/proc
|
||||
```
|
||||
|
||||
Optionally, copy DNS resolver configuration.
|
||||
|
||||
```
|
||||
sudo cp /etc/resolv.conf /path/to/chroot-env/etc/resolv.conf
|
||||
```
|
||||
|
||||
Start chrooting
|
||||
|
||||
```bash
|
||||
sudo chroot chroot-debian /bin/bash
|
||||
```
|
||||
|
||||
Once done, exit the session and unmount
|
||||
|
||||
```bash
|
||||
sudo umount chroot-debian/dev chroot-debian/proc
|
||||
```
|
||||
|
||||
#### Pass variables to chrooted environment
|
||||
|
||||
```bash
|
||||
chroot ./ env -i PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
```
|
||||
|
||||
# Allowing user to run a command as root
|
||||
|
||||
```shell
|
||||
sudo visudo
|
||||
```
|
||||
|
||||
```
|
||||
artur ALL=(ALL) chroot /path/to/chroot-env
|
||||
```
|
||||
|
||||
|
||||
#### Nginx - serving files setup DAV
|
||||
|
||||
Full version with 3rd party extensions
|
||||
|
||||
```
|
||||
apt install nginx-full nginx-extras
|
||||
```
|
||||
|
||||
```
|
||||
location / {
|
||||
index nonextistent;
|
||||
autoindex on;
|
||||
autoindex_format json;
|
||||
}
|
||||
|
||||
location /restricted {
|
||||
fancyindex on;
|
||||
fancyindex_exact_size off;
|
||||
auth_basic "Restricted";
|
||||
auth_basic_user_file "/etc/nginx/.htpasswd";
|
||||
}
|
||||
|
||||
location /dropbox {
|
||||
index nonextistent;
|
||||
autoindex on;
|
||||
autoindex_format json;
|
||||
|
||||
dav_methods PUT DELETE MKCOL COPY MOVE;
|
||||
dav_ext_methods PROPFIND OPTIONS LOCK UNLOCK;
|
||||
dav_access user:rw group:r all:r;
|
||||
|
||||
# client_max_body_size 0;
|
||||
create_full_put_path on;
|
||||
client_body_temp_path /tmp/;
|
||||
|
||||
limit_except GET PROPFIND OPTIONS HEAD {
|
||||
auth_basic "Restricted";
|
||||
auth_basic_user_file "/etc/nginx/.htpasswd";
|
||||
}
|
||||
# auth_pam "Restricted";
|
||||
# auth_pam_service_name "common-auth";
|
||||
}
|
||||
```
|
||||
|
||||
Create password
|
||||
|
||||
``` bash
|
||||
echo -n 'sammy:' >> /etc/nginx/.htpasswd
|
||||
openssl passwd -apr1 >> /etc/nginx/.htpasswd
|
||||
```
|
||||
|
||||
Another method to set the password
|
||||
|
||||
```bash
|
||||
htpasswd -c /etc/nginx/.htpasswd sammy
|
||||
htpasswd /etc/nginx/.htpasswd another_user
|
||||
```
|
||||
|
||||
**UI Client `Cyberduck`**
|
||||
|
||||
#### Backing up the entire OS
|
||||
|
||||
```
|
||||
sudo rsync -aAXHv / --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found"} /mnt
|
||||
```
|
||||
|
||||
Python
|
||||
|
||||
```
|
||||
install python3-full
|
||||
```
|
||||
|
||||
### Check battery
|
||||
|
||||
```bash
|
||||
upower -i /org/freedesktop/UPower/devices/battery_BAT0
|
||||
upower -i `upower -e | grep 'BAT'`
|
||||
upower -i $(upower -e | grep BAT) | grep --color=never -E "state|to\ full|to\ empty|percentage"
|
||||
```
|
||||
|
||||
Battery capacity
|
||||
|
||||
```bash
|
||||
cat /sys/class/power_supply/BAT0/capacity
|
||||
```
|
||||
|
||||
`apt install acpi`
|
||||
|
||||
acpi -V
|
||||
acpi -t
|
||||
|
||||
Watch the status for example:
|
||||
|
||||
```
|
||||
watch --interval=5 acpi -V
|
||||
```
|
||||
|
||||
### How to scan open ports
|
||||
|
||||
```
|
||||
nmap -sT -p- 10.10.8.8
|
||||
nmap -p 80 127.0.0.1
|
||||
```
|
||||
|
||||
|
||||
### Remove non ASCII characters form the file
|
||||
|
||||
```bash
|
||||
sed 's/[^[:print:]\t]//g' script.sh > cleaned_script.sh
|
||||
```
|
||||
|
||||
## Network Manager
|
||||
|
||||
Check Status
|
||||
|
||||
```
|
||||
pacman -S networkmanager
|
||||
systemctl status NetworkManager
|
||||
```
|
||||
|
||||
```bash
|
||||
nmcli dev status
|
||||
nmcli radio wifi
|
||||
```
|
||||
|
||||
```bash
|
||||
nmcli radio wifi on
|
||||
```
|
||||
|
||||
```bash
|
||||
nmcli dev wifi list
|
||||
```
|
||||
|
||||
```bash
|
||||
nmcli dev wifi connect network-ssid
|
||||
```
|
||||
|
||||
```bash
|
||||
nmcli dev wifi connect network-ssid password "network-password"
|
||||
|
||||
# with password promot
|
||||
nmcli dev wifi connect network-ssid password "network-password"
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
nmcli con show
|
||||
```
|
||||
|
||||
```
|
||||
nmcli con down ssid/uuid
|
||||
nmcli con up ssid/uuid
|
||||
```
|
||||
|
||||
```
|
||||
ip link set eno1 up
|
||||
```
|
||||
|
||||
### DHCP Client
|
||||
|
||||
```
|
||||
pacman -S dhclient dhcpcd
|
||||
```
|
||||
|
||||
Turn dchp on for the interface
|
||||
|
||||
```
|
||||
dhclient eth0 -v
|
||||
```
|
||||
|
||||
|
||||
### Create file
|
||||
|
||||
```bash
|
||||
cat > filename <<- "EOF"
|
||||
file contents
|
||||
more contents
|
||||
EOF
|
||||
```
|
||||
|
||||
|
||||
### Get data about file include number of links
|
||||
|
||||
```bash
|
||||
stat f1
|
||||
```
|
||||
|
||||
### File decryption
|
||||
|
||||
```bash
|
||||
openssl aes256 -md sha256 -d -in file.enc.zip -out file.zip -pass pass:"<password>"
|
||||
```
|
||||
|
||||
|
||||
### Base64
|
||||
|
||||
```bash
|
||||
openssl base64 -in qrcode.png -out qrcode.png.base64
|
||||
openssl base64 -in qrcode.png
|
||||
```
|
||||
|
||||
|
||||
### tar.xz
|
||||
|
||||
```
|
||||
tar -xJf file.tar.xz -C destination
|
||||
```
|
||||
|
||||
|
||||
## Execute command
|
||||
|
||||
Execute `ls` for each each object in current directory that starts from `D` or `M`
|
||||
|
||||
```
|
||||
ls {D*,M*}
|
||||
```
|
||||
|
||||
Utworzenie dwóch katalogów `test_a`, `test_b`
|
||||
|
||||
```
|
||||
mkdir test_{a,b}
|
||||
```
|
||||
|
||||
### copy content of directory and merge it. Dereference links
|
||||
|
||||
```
|
||||
cp -LTr /form/ .
|
||||
```
|
||||
|
||||
## JSON Linux apis
|
||||
|
||||
```
|
||||
tree -J
|
||||
ls -l | jq -R -s -c 'split("\n")[:-1]'
|
||||
```
|
||||
|
||||
|
||||
### List dependencies
|
||||
|
||||
```bash
|
||||
pacman -Sy lld
|
||||
llvm-nm
|
||||
```
|
||||
|
||||
### Find location of executable
|
||||
|
||||
```
|
||||
type -a python
|
||||
```
|
||||
|
||||
### Reload each file save
|
||||
|
||||
```bash
|
||||
brew install fswatch
|
||||
fswatch -o . --exclude "\.build.*$" | xargs -n1 -I{} your-command
|
||||
```
|
||||
|
||||
|
||||
## Add capabilities
|
||||
allow all to open 443 port
|
||||
|
||||
```bash
|
||||
sudo setcap 'cap_net_bind_service=+ep' ./service
|
||||
```
|
||||
|
||||
|
||||
# Test speed of the drive
|
||||
|
||||
```
|
||||
sudo hdparam -t --direct /dev/mmcblk0
|
||||
```
|
12
utils/regex.md
Normal file
12
utils/regex.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
layout: default
|
||||
title: Regex - notes
|
||||
---
|
||||
|
||||
How to match “any character” in the regular expression
|
||||
|
||||
- `.` → any char except newline
|
||||
- `\.` → the actual dot character
|
||||
- `.?` = `.{0,1}` → match any char except newline zero or one times
|
||||
- `.*` = `.{0,}` → match any char except newline zero or more times
|
||||
- `.+` = `.{1,}` → match any char except newline one or more times
|
372
utils/vim.md
Normal file
372
utils/vim.md
Normal file
|
@ -0,0 +1,372 @@
|
|||
---
|
||||
layout: default
|
||||
title: Vim notes
|
||||
categories: software
|
||||
---
|
||||
|
||||
#### Symbols and meanings
|
||||
- `%` → current file. An example: `:so %` → Source the current file
|
||||
- `$` → end of line
|
||||
- `.` → Current line An example: `:.!sh` → Pipe current line to `sh` and replace it with the output
|
||||
|
||||
Entering `!!` in normal mode is translated to `:.!` I. e. Typing `!!date` in normal mode replaces current line with the date.
|
||||
|
||||
#### Tips
|
||||
|
||||
- `:e[dit]` → Edit the current file. This is useful to re-edit the current file, when it has been changed outside of Vim. `:e!` Force reload file
|
||||
- `:help index` → Get all default mappings
|
||||
|
||||
#### Navigation
|
||||
|
||||
- `h` `j` `k` `l` → left, down, up, right
|
||||
- `*` → Next whole word under cursor (previous `#`)
|
||||
- `e` → Forward to the end of word. `E` can contain punctuation
|
||||
- `w` → Move forward to the beginning of a word. `W` Same as `w`, but special characters are treated as part of a word.
|
||||
- `b` → Works as `w`, but backwards
|
||||
- `{`,`}` → Jump by paragraphs
|
||||
- `(`,`)` → Jump by sentences
|
||||
- `G` → Jump to the end of the file
|
||||
- `1G` → Jump to the beginning of the file (same as `gg`)
|
||||
- `50G` → Jump to line 50
|
||||
- `0` → Beginning of line
|
||||
- `_` or `^` → first non-blank character of the line
|
||||
- `g_` → last non-blank character of the line
|
||||
- `fX` → next character `X`. `FX` previous. `;` repeat , `,` repeat in reverse
|
||||
- `tX` → tili next `X` (similar to above, but the cursor is before `X`)
|
||||
- `H` → Jump to the top of the screen
|
||||
- `M` → Jump to the middle of the screen
|
||||
- `L` → Jump to the bottom of the screen
|
||||
|
||||
#### Scrolling
|
||||
|
||||
* `10 <PageUp>` or `10<CTRL-B>` → Move 10 pages up
|
||||
* `5 <PageDown>` or `5<CTRL-F>` → Move 5 pages down.
|
||||
- `zz` → scroll the line with the cursor to the center of the screen
|
||||
- `zt` → to the top
|
||||
- `zb` → to the bottom
|
||||
|
||||
### Terminal buffers
|
||||
|
||||
- `:te[rm[inal]] command`
|
||||
- `:b#` switch buffer
|
||||
- `:ls` list buffers
|
||||
- `:buff 1` or `:b1` switch to buffer 1
|
||||
|
||||
### List of the commands
|
||||
|
||||
Common meaning of letters in the commands
|
||||
- `w` → word
|
||||
- `i` → inner
|
||||
|
||||
| Command | |
|
||||
| ---: | :--- |
|
||||
| `dd` | Delete one line |
|
||||
| `d` | Delete selection |
|
||||
| `x` | Delete character under cursor |
|
||||
| `d+` | Delete 2 lines |
|
||||
| `:%d` or :`1,$d` | Delete the whole of the file |
|
||||
| `dw`, `diw` | Delete what that the cursor is over |
|
||||
| `di(` | Delete inner brackets. `da(` → including brackets |
|
||||
| `:r[ead] !date` | Execute commend and put content into editor |
|
||||
| `.` | Repeat the last operation |
|
||||
| `gU` | Uppercase the selection, `gu` → lower |
|
||||
| `%` | Jump to matching bracket `{ }` `[ ]` `( )` |
|
||||
| `:%!column -t` | Put text in columns |
|
||||
| `:%!sort` | Sort the whole file |
|
||||
| `:'<,'>!grep text` | Keep lines that contains `text` |
|
||||
| `:'<,'>!sort` | Sort selected lines |
|
||||
| `:eariler 1m` | State from the 1 min before |
|
||||
| `ga` | Display hex, ascii value of character under cursor |
|
||||
| `g8 ` | Display hex value of utf-8 character under cursor |
|
||||
| `ciw` | Change inner word |
|
||||
| `yiw` | Yank inner word |
|
||||
| `viwp` | Select word and then replace it with previously yanked text |
|
||||
| `rX` | replace every character in selection or under cursor with `X` |
|
||||
| `guiw` | Lower case word |
|
||||
| `guu` | Lowercase line |
|
||||
| `gUU` | Uppercase line |
|
||||
| `=` | Indent the selection |
|
||||
| `=%` | Indent the current braces |
|
||||
| `G=gg` | indent entire document |
|
||||
| `ZZ` | Write current file, if modified, and exit (same as `:wq`) |
|
||||
| `ZQ` | Quit current file and exit (same as `:q!`) |
|
||||
| `:so %` | Source current file |
|
||||
| `@:` | Execute last command again |
|
||||
|
||||
#### Status line
|
||||
|
||||
```vim
|
||||
:set statusline=%<%f%h%m%r%=%b\ 0x%B\ \ %l,%c%V\ %P
|
||||
```
|
||||
|
||||
#### Visual Mode
|
||||
|
||||
- `V` → Selection by lines
|
||||
- `v` → Selection follows the cursor
|
||||
- `Ctrl+v` → Block selection
|
||||
- When selected block you can applay changes to each line by typing `I` editing and finally pressing `Esc`
|
||||
|
||||
#### Enter Insert Mode
|
||||
|
||||
- `I` → At the first non white character of the line
|
||||
- `i` → On the left of the cursor
|
||||
- `A` → At the very end of the line
|
||||
- `a` → On the right of the cursor
|
||||
- `c` → Delete selection and enter insert mode
|
||||
- `o` → Create new line below and enter insert mode
|
||||
- `O` → Create new line above and enter insert mode
|
||||
|
||||
#### Split the editor
|
||||
|
||||
- `:sp <filename>` → Vertically
|
||||
- `:vs <filename>` → Horizontally
|
||||
- `:set splitbelow`, `:set splitright`
|
||||
|
||||
#### Markers
|
||||
|
||||
- `:marks` → list of marks
|
||||
- `ma` → set current position for mark `a`
|
||||
- `` `a`` → jump to the cursor position of mark `a`
|
||||
- `'a` → jump to the beginning of a line of a mark `a`
|
||||
- ``y`a`` → yank text to position of mark `a`
|
||||
* ``` `` ``` → Return to the cursor position before the latest jump
|
||||
* `` `.`` → Jump to the last changed line.
|
||||
|
||||
### Recording macros
|
||||
|
||||
1. `qa` → Start recording macro under letter `a`
|
||||
2. `q` → Stop recording
|
||||
3. `@a` → Play the macro saved under letter a
|
||||
4. `@@` → Play the last macro
|
||||
|
||||
#### Searching
|
||||
|
||||
- `:%s/Plug.*$//` → Search and delete all lines that starts from Plug
|
||||
- `:%s/foo/bar/gc` → Replace all occurrence of foo by bar with confirmation
|
||||
- `'<,'>:s/find/replacewith/` Replace selection
|
||||
- `/pattern` → search for pattern then enter and `n` next `N` previous match
|
||||
- `?pattern` → search backward for pattern
|
||||
|
||||
### Registers
|
||||
|
||||
- `:reg` → print all registers
|
||||
- `"ap` → paste the register `a` , if the macro is recorded then it will paste it
|
||||
- `"xy` → yank into register `x`
|
||||
- `:let @a = "kkll"` → set a macro from the command mode
|
||||
- `:let @A='i'` → append to register `a`
|
||||
- `:%normal @a` → execute the macro on all lines of the current file
|
||||
- `:'<,'>normal @a` → execute the macro on a visually selected lines
|
||||
- `:10,20 normal @a` → execute the macro for lines from 10 to 20
|
||||
- `:g/pattern/ normal @a` → Search for pattern and execute macro for it
|
||||
|
||||
#### Functions - An example
|
||||
|
||||
Function definition
|
||||
|
||||
```vim
|
||||
function! CalculateAge()
|
||||
normal 03wdei^R=2012-^R"^M^[0j
|
||||
endfunction
|
||||
```
|
||||
|
||||
Key banding to function
|
||||
|
||||
```vim
|
||||
nnoremap <leader>a :call CalculateAge()<CR>
|
||||
```
|
||||
|
||||
Preloading vim with macros like
|
||||
|
||||
```vim
|
||||
let @a='03wdei^R=2012-^R"^M^[0j'
|
||||
```
|
||||
|
||||
Call function from the command mode
|
||||
|
||||
```
|
||||
:call CalculateAge()
|
||||
```
|
||||
|
||||
#### Configuration
|
||||
|
||||
The config file is located at `.config/nvim/init.vim`
|
||||
|
||||
```vim
|
||||
if empty(glob('~/.local/share/nvim/site/autoload/plug.vim'))
|
||||
silent !curl -fLo ~/.local/share/nvim/site/autoload/plug.vim --create-dirs
|
||||
\ https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
|
||||
autocmd VimEnter * PlugInstall --sync | source $MYVIMRC
|
||||
endif
|
||||
|
||||
call plug#begin('~/.vim/plugged')
|
||||
|
||||
Plug 'junegunn/fzf'
|
||||
|
||||
Plug 'neovim/nvim-lspconfig'
|
||||
Plug 'neoclide/coc.nvim', {'branch': 'release'}
|
||||
|
||||
Plug 'vim-airline/vim-airline'
|
||||
Plug 'vim-airline/vim-airline-themes'
|
||||
|
||||
Plug 'vim-syntastic/syntastic'
|
||||
Plug 'tokorom/syntastic-swiftlint.vim'
|
||||
|
||||
call plug#end()
|
||||
```
|
||||
|
||||
- `set relativenumber`
|
||||
- `set encoding=utf-8`
|
||||
- `syntax on`
|
||||
- `map za :FZF<CR>` → fuzzy finder over `za`
|
||||
|
||||
Indentation setup
|
||||
|
||||
- `set tabstop=2 shiftwidth=2 expandtab`
|
||||
- `filetype plugin indent on`
|
||||
|
||||
```vim
|
||||
let g:syntastic_swift_checkers = ['swiftlint', 'swiftpm']
|
||||
lua << EOF
|
||||
local lspconfig = require('lspconfig')
|
||||
lspconfig.sourcekit.setup{}
|
||||
EOF
|
||||
```
|
||||
|
||||
#### Rename current file
|
||||
|
||||
|
||||
```vim
|
||||
|
||||
function! RenameFile()
|
||||
let old_name = expand('%')
|
||||
let new_name = input('New file name: ', expand('%'), 'file')
|
||||
if new_name != '' && new_name != old_name
|
||||
exec ':saveas ' . new_name
|
||||
exec ':silent !rm ' . old_name
|
||||
exec ':bd ' . old_file
|
||||
redraw!
|
||||
endif
|
||||
endfunction
|
||||
map <leader>n :call RenameFile()<cr>
|
||||
```
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
### Navigation in Vimr
|
||||
- jump to the next empty line (the next paragraph)`}`
|
||||
* Recording macros
|
||||
* `"xp` pastes the contents of the register `x`.
|
||||
|
||||
|
||||
* `~ ` : invert case (upper->lower; lower->upper) of current character
|
||||
* `gf ` : open file name under cursor (SUPER)
|
||||
|
||||
* `ggg?G` : rot13 whole file
|
||||
* `xp` : swap next two characters around
|
||||
* `CTRL-A,CTRL-X` : increment, decrement next number on same line as the cursor
|
||||
* `CTRL-R=5*5` : insert 25 into text
|
||||
|
||||
|
||||
|
||||
* `'.` : jump to last modification line (SUPER)
|
||||
* *`.* : jump to exact spot in last modification line
|
||||
* `<C-O>` : retrace your movements in file (backward)
|
||||
* `<C-I>` : retrace your movements in file (forward)
|
||||
* `:ju(mps)` : list of your movements {{help|jump-motions}}
|
||||
:history : list of all your commands
|
||||
|
||||
|
||||
Sorting with external sort
|
||||
:%!sort -u : contents of the current file is sorted and only unique lines are kept
|
||||
:'v,'w!sort : sort from line marked v thru lines marked w
|
||||
:g/^$/;,/^$/-1!sort : sort each block (note the crucial ;)
|
||||
|
||||
!1} sort : sorts paragraph; this is issued from normal mode!)
|
||||
|
||||
:wn : write file and move to next (SUPER)
|
||||
:bd : remove file from buffer list (SUPER)
|
||||
:sav php.html : Save current file as php.html and "move" to php.html
|
||||
:w /some/path/%:r : save file in another directory, but with the same name
|
||||
:e # : edit alternative file
|
||||
:args : display argument list
|
||||
:n : next file in argument list
|
||||
:prev : previous file in argument list
|
||||
:rew : rewind to first file in argument list
|
||||
:ls : display buffer list
|
||||
:bn : next buffer
|
||||
:bp : previous buffer
|
||||
:brew : rewind to first buffer in buffer list
|
||||
:tabe : open new tab page (Ctrl-PgUp, Ctrl-PgDown for next/previous tab)
|
||||
:tabm n : move tab to position n (0=leftmost position)
|
||||
|
||||
# editing a register/recording
|
||||
"ap
|
||||
<you can now see register contents, edit as required>
|
||||
"add
|
||||
@a
|
||||
|
||||
|
||||
|
||||
|
||||
Ctrl-D move half-page down
|
||||
Ctrl-U move half-page up
|
||||
Ctrl-B page up
|
||||
Ctrl-F page down
|
||||
Ctrl-O jump to last (older) cursor position
|
||||
Ctrl-I jump to next cursor position (after Ctrl-O)
|
||||
Ctrl-Y move view pane up
|
||||
Ctrl-E move view pane down
|
||||
|
||||
n next matching search pattern
|
||||
N previous matching search pattern
|
||||
|
||||
|
||||
g* next matching search (not whole word) pattern under cursor
|
||||
g# previous matching search (not whole word) pattern under cursor
|
||||
|
||||
de — Delete to the end of the word
|
||||
^R= — Insert the contents of the special = register, which accepts an expression to evaluate
|
||||
|
||||
gUgn - uppercase
|
||||
gn
|
||||
n move to the next match
|
||||
|
||||
|
||||
Undo and redo
|
||||
|
||||
You can use u to undo the last change. CTRL-R redoes a change that has been undone. U returns the current line to its original state.
|
||||
You can use g- or g+ to go between text-states.
|
||||
Search and replace
|
||||
* `\vpattern` - 'very magic' pattern: non-alphanumeric characters are interpreted as special regex symbols (no escaping needed)
|
||||
* `n` - repeat search in same direction
|
||||
* `N` - repeat search in opposite direction
|
||||
|
||||
* `:noh` - remove highlighting of search matches
|
||||
Search in multiple files
|
||||
* `:vimgrep /pattern/ {file}` - search for pattern in multiple files
|
||||
|
||||
* e.g. `:vimgrep /foo/ **/*`
|
||||
* `:cn` - jump to the next match
|
||||
* `:cp` - jump to the previous match
|
||||
* `:copen` - open a window containing the list of matches
|
||||
|
||||
|
||||
folding from selection
|
||||
`: '<,'>fo`
|
||||
|
||||
`:help folding`
|
||||
|
||||
set foldmethod=syntax
|
||||
`set foldlevel=1`
|
||||
`set foldclose=all`
|
||||
|
||||
|
||||
Sequence forfolding lines `Shift+V:fo`
|
||||
`:set foldmethod=syntax` intent,
|
||||
`zo` unfolding
|
||||
`za` toogle folding
|
||||
`zf#j` creates a fold from the cursor down # lines.
|
||||
|
||||
:CheckHealth
|
||||
-->
|
Loading…
Add table
Add a link
Reference in a new issue